Guide

Git from your phone over SSH: the workflow that actually works (2026)

The reliable way to use Git from a phone is not to run Git on the phone. SSH into the machine that already has the repo — a VPS, a dev box, your Mac on Tailscale — and run Git there, where the credentials, the toolchain and the network already work. Here's the setup, the GitHub authentication choices, and the commands worth typing on a 40-column screen.

CC Chen Chen· Founder·2026-08-24·12 min read

The short answer

Use Git from your phone by SSHing into the machine that already has the repo, and running Git there. A VPS, a dev box at the office, your Mac at home on a tailnet — anywhere the clone, the deploy keys, the toolchain and the CI config already live. The phone contributes a keyboard and a screen; it does not become a second workstation you have to keep in sync.

This is the opposite of what most “Git on iPhone” articles suggest, and the reason is boring: the hard part of Git isn’t Git, it’s everything around the clone. Credentials, .env files, node_modules, a Docker daemon, a test runner, the LFS objects. Cloning to a handset means reproducing all of that on a device with a sandboxed filesystem, no package manager worth the name, and an OS that suspends your app in half a minute.

The two things you genuinely need to get right are covered below: how the remote box authenticates to GitHub (deploy key, machine key, or a carefully-scoped agent forward — not ForwardAgent yes), and which Git commands are worth typing on a narrow screen (a lot of -s, --oneline and --no-pager; almost no interactive rebase).

If you’d rather have the clone on the phone itself, that’s a legitimate choice for notes repos, static sites and Obsidian vaults — Working Copy on iOS is genuinely excellent at it. It’s the wrong shape for shipping application code.

Two approaches, and which one to pick

Git on a remote box (SSH)Git on the phone
What runs whereRepo, keys, toolchain all remote; phone is a terminalFull clone on the device
Credentials to manageOne key, on the serverA second set, on the phone, on GitHub
Build/test after a changeYes — it’s a real machineNo
Works offlineNoYes
Merge conflict resolutionWhatever editor is on the boxThe app’s diff UI (often better than a phone vim)
CostWhatever the box costsWorking Copy pro unlock is a one-off $35.99; Termux is free
Good forApplication code, hotfixes, ops, deploysNotes, blogs, static sites, config repos, Obsidian vaults
Falls apart whenYou have no box and no networkThe repo needs a toolchain, secrets, or LFS

Concretely, the on-device options in 2026:

  • Working Copy (iOS/iPadOS) — the serious one. Free download; pushing to a remote and managing more than five repositories require the pro unlock, a one-off in-app purchase of $35.99 with a 10-day trial, and it’s free for verified students via GitHub Student. Real SSH key generation, a proper diff/merge UI, and a Files provider so other editors can work in the checkout.
  • a-Shell (iOS) — ships lg2, a libgit2-based mini-Git. Clone, commit and push work; anything past that (rebase, submodules, hooks) doesn’t exist. Free.
  • Termux (Android) — pkg install git gives you actual upstream Git plus OpenSSH, which is why it’s still the answer for on-device Android Git. It’s also a Linux userland you now maintain on your phone; see Termux alternatives for when that’s the wrong trade.
  • iSH (iOS) — Alpine under x86 emulation. apk add git works. It is slow enough that cloning any real repo is an exercise in patience.

Everything below assumes the SSH approach.

Setting it up: repo on a box, phone on SSH

Four pieces, once.

1. A box that’s reachable. If it’s a public VPS, you already have this. If it’s your desktop or a home server behind NAT, put it and the phone on a tailnet — that’s the whole reason TermAI ships Tailscale in-app rather than making you juggle two apps and one VPN slot. Setup is in Tailscale SSH from a phone.

2. A key on the phone, in the phone’s secure storage, on the server’s authorized_keys. Ed25519, generated on-device, never copied anywhere (create an SSH key on iPhone, ed25519 vs RSA). Restrict it in authorized_keys — a phone key should be a scalpel:

restrict,pty,from="100.64.0.0/10" ssh-ed25519 AAAAC3Nz… chen-iphone-2026-08

restrict (added in OpenSSH 7.2) turns off port forwarding, agent forwarding, X11 and pty allocation in one word; pty puts the terminal back. The from= limits the key to tailnet addresses. More of this reasoning in mobile SSH production safety.

3. A host entry so connecting is one tap. In the client’s saved-host UI, or in ~/.ssh/config if your client reads one (SSH config file):

Host dev
  HostName dev.tail1234.ts.net
  User chen
  IdentityFile ~/.ssh/id_ed25519_phone
  ServerAliveInterval 30
  ServerAliveCountMax 3

ServerAliveInterval 30 matters more on mobile than anywhere else. Cellular NAT tables drop idle mappings aggressively, and without keepalives you’ll discover the session is dead only when you press Enter on a git push. Same fix as SSH broken pipe.

4. Land in tmux, not in a bare shell. This is the single highest-value habit for mobile Git:

tmux new -A -s dev

-A attaches to the session named dev if it exists and creates it otherwise, so the same command works every time. Now a git push of a large repo, a git clone, or a rebase you’re halfway through survives you walking into a lift. Without it, a dropped connection during git push leaves you guessing whether the objects made it. Details in keeping a process running after SSH.

Getting the server authenticated to GitHub

This is where people wire it up wrong. You are on your phone, SSHed into dev, and dev now needs to push to GitHub. Four options, in the order you should consider them:

1. A deploy key — best when the box serves one repo. A key registered on a single repository rather than on your account. Read-only by default; write access is a checkbox you tick deliberately. Generate on the server, paste the public half into Settings → Deploy keys on that repo:

ssh-keygen -t ed25519 -C "dev-box deploy key" -f ~/.ssh/id_ed25519_gh
cat ~/.ssh/id_ed25519_gh.pub

The blast radius is one repo. If the box is compromised, that’s the whole loss. A deploy key can be used on only one repository at a time across your account, which is the constraint people hit first.

2. A user key on the box — pragmatic for a personal dev box. Same ssh-keygen, but you add the public key under Settings → SSH and GPG keys on your account. Now the box can act as you on everything you can reach. Fine for a machine only you touch; not fine for anything shared.

3. A machine user — the right answer for shared or team boxes. A dedicated GitHub account with its own key, invited to exactly the repos it needs. More setup, correct isolation.

4. Agent forwarding — powerful, and the one to be careful with. ssh -A dev exports your phone’s agent socket to the server, so the server can authenticate to GitHub as you without ever holding a key. That’s genuinely attractive: no key material on the box at all. The catch is well documented — anyone who can bypass file permissions on that server (root, or a compromised process running as you) can use the forwarded socket to authenticate as you to anything your key opens for as long as the session lives. They can’t extract the key; they don’t need to. OpenSSH’s own documentation flags this, and the practical rule is to make it a per-session decision:

ssh -A dev        # yes, for this one session

…and never this:

Host *
  ForwardAgent yes   # don't

There’s precedent beyond theory: CVE-2023-38408, disclosed July 2023, let a malicious server run arbitrary code on the client via the forwarded agent in OpenSSH 5.5 through 9.3p1. Forwarding is an attack surface, not just a convenience.

Whichever you pick, verify it before you need it. From the server:

ssh -T [email protected]

Success looks like this:

Hi chen! You've successfully authenticated, but GitHub does not provide shell access.

That is the success message, not an error. GitHub accepts Git protocol commands on that endpoint and nothing else, so it closes the session — and, unhelpfully, exits with status 1 even when everything worked. Read the sentence, not the exit code.

Then point the remote at SSH rather than HTTPS, since GitHub removed password authentication for Git over HTTPS in August 2021 and HTTPS now means juggling a personal access token:

git remote set-url origin [email protected]:you/repo.git

Verifying GitHub’s host key from a phone

The first Git command over SSH on a fresh box asks you to trust GitHub’s host key. On a laptop you’d compare against the docs page. On a phone, opening a browser to check a base64 string is exactly the moment people give up and type yes blind.

GitHub’s current fingerprints (docs):

Key typeSHA256 fingerprint
Ed25519SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU
RSASHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s

The better move on a phone is not to compare by eye at all. Since 18 January 2022 GitHub publishes its host keys in the API, so you can populate known_hosts from the source over TLS:

curl -s https://api.github.com/meta \
  | jq -r '.ssh_keys[] | "github.com " + .' >> ~/.ssh/known_hosts

One line, no transcription errors, no browser.

If you get REMOTE HOST IDENTIFICATION HAS CHANGED for github.com specifically, the likely cause is history rather than an attack: GitHub replaced its RSA host key on 24 March 2023 at around 05:00 UTC after discovering that the private key had been briefly exposed in a public repository (GitHub blog). Machines with a known_hosts older than that still carry the retired key. Remove the stale entry, then re-add from the API:

ssh-keygen -R github.com

General case: host key verification failed.

When port 22 is blocked

Hotel Wi-Fi, corporate guest networks and a fair number of captive portals block outbound 22. Your SSH connection to your own box is the first casualty — a different problem, usually solved by running sshd on 443 or 2222. But once you’re on the box and it can’t reach github.com:22, GitHub has an official second door.

The hostname changes, not just the port. It’s ssh.github.com, not github.com (docs):

ssh -T -p 443 [email protected]

Make it permanent in ~/.ssh/config so every existing remote keeps working unchanged:

Host github.com
  Hostname ssh.github.com
  Port 443
  User git

The Ed25519 host key fingerprint for ssh.github.com on 443 is the same SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU. Note this is plain SSH on port 443, not SSH tunnelled inside TLS — a proxy doing deep packet inspection can still tell the difference and block it.

Git commands that survive a phone screen

A phone in portrait is roughly 40 columns. Git’s default output assumes 80+ and a pager you can scroll with a mouse. Three configuration changes and a handful of habits make the difference between usable and miserable.

Kill the pager for short output. less on a touchscreen is a bad time — you get a full-screen mode you then have to remember how to quit.

git config --global core.pager 'less -FX'

-F quits immediately if the output fits on one screen; -X stops it clearing the screen on exit, so the result stays in your scrollback where you can select and copy it. For a one-off, git --no-pager log ….

Use the short forms. They’re not just shorter to type, they’re narrower to read:

Instead ofTypeWhy
git statusgit status -sbTwo-column porcelain plus branch line; ~10 lines instead of 30
git loggit log --oneline -10Fits 40 columns; one commit per line
git diffgit diff --statWhich files changed, before you commit to reading hunks
git branch -agit branch --sort=-committerdate | headMost recent branches first, not alphabetical
git show HEADgit show --stat HEADSummary first; drill down only if needed

Set aliases on the server once. You’re typing on glass; every saved character counts.

git config --global alias.s 'status -sb'
git config --global alias.l 'log --oneline -12'
git config --global alias.d 'diff --stat'
git config --global alias.p 'pull --ff-only'

pull --ff-only as the default p is deliberate: it refuses rather than creating a surprise merge commit when the branch has diverged. Discovering divergence via an error message beats discovering it via a merge you didn’t intend and now have to unpick on a phone.

Expect Unicode breakage. Git’s status output, --graph lines and many shell prompts use box-drawing and Powerline glyphs. If yours render as tofu boxes, that’s a font problem on the client, not a Git problem — see Nerd Fonts over mobile SSH.

Use the AI assistant for the commands you can’t remember. This is the honest use case for AI in a mobile terminal: not “run my deploy”, but “what’s the command to undo the last commit but keep the changes staged” → git reset --soft HEAD~1, shown to you, run only after you tap confirm. Git’s CLI is famously full of commands whose names don’t match what they do, and recalling the difference between reset --soft, reset --hard and restore --staged on a phone at 2am is exactly the wrong test of memory. TermAI’s assistant proposes; you approve. That distinction is the whole argument in assistant vs agent, and it matters more for Git than for almost anything else, because git reset --hard has no undo button a beginner can find.

Committing without a real editor

git commit with no -m opens $EDITOR. On a phone that means vim or nano over SSH, on a keyboard with no Escape key and no Ctrl, inside a session iOS may suspend. It’s survivable but it isn’t where you want to be.

Always pass -m. For a multi-paragraph message, repeat the flag — each -m becomes a paragraph:

git commit -m "fix: reject empty webhook payloads" -m "Returned 500 instead of 400 when body was empty. Closes #412."

Add --no-edit to the commands that ambush you with an editor:

git pull --no-edit              # if a merge commit is unavoidable
git merge --no-edit feature
git commit --amend --no-edit    # amend content, keep the message

Set a sane editor anyway, for when you can’t avoid it. nano is far kinder than vim on a phone — Ctrl-O and Ctrl-X are printed on screen, and most mobile SSH clients put a Ctrl key on their accessory bar even when the system keyboard has none:

git config --global core.editor nano

Staging is the real friction, not the message. git add -p is an interactive loop of single-key answers (y, n, s, q) — actually one of the better mobile Git experiences, since every answer is one character. git add -e, which opens the patch itself in an editor, is not. Prefer git add -p or explicit paths; skip -e.

Two habits worth keeping: check what you’re about to commit with git diff --cached --stat before you push, and accept that your commit message quality will drop on a phone. If the change deserves a paragraph and you can’t face typing it, commit with a short message and amend it later from a laptop — git commit --amend before pushing costs nothing.

Longer-form work — actually writing the code, not just committing it — is a different question, and increasingly answered by running an AI coding tool on the box: Claude Code from your phone and Codex CLI from your phone both cover that pattern, and both are the same tmux-over-SSH shape as this.

Signing commits from a phone

If your repo enforces signed commits, a phone-driven workflow has to sign too, and GPG on a mobile-accessed box is unpleasant. SSH signing is the better path.

Git 2.34 (released 15 November 2021) added the ability to sign objects with an SSH key instead of PGP, and GitHub has verified those signatures since 23 August 2022 (changelog). On the box you SSH into:

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519_gh.pub
git config --global commit.gpgsign true

Then upload that same public key to GitHub a second time, as a Signing Key rather than an Authentication Key — they’re separate entries in SSH and GPG keys even for an identical key, and there’s no limit on how many signing keys you add.

Two things that bite:

  • ssh-keygen from OpenSSH 8.7 has broken signing support. The Git 2.34 release notes say so explicitly. Use 8.8 or later — check with ssh -V on the box.
  • Local verification needs an allowed-signers file. Without gpg.ssh.allowedSignersFile, git log --show-signature can’t tell you who signed anything; GitHub’s web UI will still show Verified.

Can the signing key live on the phone instead? Yes — a Secure Enclave–backed ecdsa-sk key, gated by Face ID, forwarded to the box via agent forwarding, so no signing key exists on the server at all. It’s the most defensible version of this and also the one with the most moving parts, and it means turning agent forwarding on, with the caveats above. How Secure Enclave SSH keys actually work covers what that key can and can’t do.

What’s safe to do from a phone, and what isn’t

The constraint isn’t Git’s capability. It’s that a mobile session is interruptible — iOS suspends a backgrounded app in roughly 20–30 seconds, cellular hands off, the lift doors close — and that recovering from a half-finished Git operation is more typing than the operation itself.

OperationOn a phoneWhy
git pull --ff-only, git log, git diff, git show✅ FineRead-only, idempotent, no half-states
git commit -m, git push✅ FineAtomic; a dropped push either landed or didn’t, and re-running is safe
git checkout -b, git switch✅ FineLocal, reversible
git stash / git stash pop⚠️ CarefulEasy to strand work in the stash and forget it exists
git merge with conflicts⚠️ CarefulConflict resolution is editor work on a 40-column screen
git rebase -i❌ AvoidEditor-driven, multi-step, leaves the repo mid-operation if the session dies
git push --force❌ AvoidUse --force-with-lease, or wait for a laptop
git reset --hard❌ AvoidThe one Git command with no easy undo. Never on a small screen at speed
git clean -fdx❌ AvoidDeletes untracked files including .env. Nothing recovers it

Two rules that make the difference:

Run everything in tmux. Then a suspended app is an inconvenience, not an abandoned rebase. Reattach with tmux a and the operation is exactly where you left it.

Prefer --force-with-lease to --force, always, and doubly so from a phone. --force-with-lease refuses the push if the remote moved since you last fetched — precisely the situation you can’t see coming on a device where you haven’t been watching the repo all day.

And the meta-rule from mobile SSH production safety: a phone is for the small, urgent, well-understood change. Hotfix a config value, cherry-pick a revert, tag a release, check why CI failed. It’s not where you refactor. If the thing you’re about to do would make you nervous on a laptop, it’s a bad idea on glass.

Error messages, decoded

What you seeWhat it meansFix
Hi user! You've successfully authenticated, but GitHub does not provide shell access.Success. Your key worksNothing. Ignore the exit code of 1
[email protected]: Permission denied (publickey).The server has no key GitHub acceptsAdd a deploy or user key on the box, or ssh -A for that session
REMOTE HOST IDENTIFICATION HAS CHANGED for github.comUsually the March 2023 RSA rotation, not an attackssh-keygen -R github.com, re-add from api.github.com/meta
ssh: connect to host github.com port 22: Connection timed outOutbound 22 blocked on that networkssh.github.com on port 443
ERROR: The deploy key is read-onlyThe deploy key was added without write accessTick “Allow write access” on the repo’s deploy key
error: failed to push some refs … fetch firstThe remote moved ongit pull --ff-only, then push. Never reach for --force here
fatal: Need to specify how to reconcile divergent branchesGit ≥ 2.27 refusing to guess your pull strategygit config --global pull.ff only
Push hangs, then Connection closed by remote hostMobile session dropped mid-transferRun in tmux; add ServerAliveInterval 30
Agent admitted failure to sign using the keyThe forwarded agent isn’t reachable from the serverConfirm ssh -A, and that the key’s authorized_keys line isn’t restricted
error: gpg failed to sign the data with gpg.format sshssh-keygen too old or broken (8.7)ssh -V; upgrade to OpenSSH 8.8+

If the SSH connection to your own box is what’s failing rather than the Git operation, start at connection timed out or permission denied instead — Git’s errors are downstream of those and will mislead you.

FAQ

Can you use Git from a phone over SSH? Yes, and the version that works well is running Git on a remote machine rather than on the phone. You SSH into a VPS, a dev box or your desktop and run git there — the repo, the SSH keys, the language toolchain and the CI config are already on that machine, so nothing has to be replicated onto a handset. The phone is a terminal, not a workstation. Running Git locally on the phone (Working Copy on iOS, Termux on Android) is a real option, but it turns the phone into a full clone that then needs its own credentials, its own merge conflicts and its own disk.

How do I authenticate to GitHub from a server I SSH into from my phone? Generate a key on that server and register it with GitHub — don’t forward your agent by default. For a box that serves one repo, a deploy key is the tightest fit: scoped to a single repository and read-only unless you explicitly grant write access. For a general dev box you push from across many repos, register the server’s key as a user key, or use a dedicated machine-user account for anything shared. Agent forwarding works and keeps key material off the box entirely, but anyone with root on that server can use the forwarded socket to authenticate as you elsewhere while the session is open — so keep it to per-session ssh -A rather than ForwardAgent yes in your config.

Is [email protected]’s “does not provide shell access” message an error? No — it’s the success message. On a correctly configured machine, ssh -T [email protected] prints Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access. That sentence means your key worked. GitHub accepts Git protocol commands on that endpoint and never an interactive shell, so it closes the session. Confusingly the command still exits with status 1, which trips up scripts that check the exit code instead of the output.

What are GitHub’s SSH host key fingerprints? The Ed25519 fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU and the RSA one is SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s. GitHub rotated the RSA host key on 24 March 2023 after finding the private key had been briefly exposed in a public repository, which is why an old known_hosts file can produce a host key verification failure for github.com. Since January 2022 the current keys are also published at api.github.com/meta under ssh_keys, so on a phone you should fetch and append them with curl rather than transcribing a fingerprint from a docs page.

Can I sign commits from my phone? Yes, with SSH signatures. Git 2.34 (November 2021) added signing with an SSH key via gpg.format ssh, and GitHub has verified those signatures since 23 August 2022. On the remote machine, set user.signingkey to a key and commit.gpgsign to true, then upload that public key to GitHub a second time as a Signing Key — authentication and signing keys are separate entries even for the same key. If you want the signing key itself to live on the phone behind Face ID, that means a Secure Enclave ecdsa-sk key plus agent forwarding, which trades one risk for another. Note that the SSH signing support in ssh-keygen from OpenSSH 8.7 is broken; use 8.8 or later.

Should I run git rebase -i or force-push from a phone? Avoid it. Interactive rebase is editor-driven and multi-step, inside a session iOS may suspend within about 30 seconds of backgrounding, on a keyboard with no comfortable Escape or Ctrl key — and an interrupted rebase leaves the repo mid-operation, which is more typing to recover from than the rebase was worth. If you must, run it inside tmux so a dropped connection doesn’t abandon it, and use --force-with-lease rather than --force so a stale view of the remote can’t silently overwrite someone else’s push.

What about Git LFS, submodules and hooks? They’re the strongest argument for the SSH approach. LFS needs its own credential handling and pulls large binaries you don’t want on a metered phone connection; submodules multiply the credential problem by the number of submodules; and pre-commit hooks routinely shell out to linters and test runners that don’t exist on iOS at all. On a remote box all three already work, because it’s the same environment your CI runs in.

Quick facts

  • Run Git on the box, not the phone. SSH in, work inside tmux new -A -s dev, so a dropped connection never abandons an operation.
  • ssh -T [email protected] printing “does not provide shell access” is success — and it still exits 1.
  • Deploy key for one repo (read-only by default), user key for a personal box, machine user for anything shared.
  • Agent forwarding: per-session ssh -A only. Root on the remote can use your forwarded agent for the life of the session; CVE-2023-38408 (OpenSSH 5.5–9.3p1) made that concrete.
  • GitHub fingerprints: Ed25519 SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU, RSA SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s. Populate known_hosts from api.github.com/meta instead of typing them.
  • RSA host key rotated 24 March 2023 after brief exposure — the usual cause of HOST IDENTIFICATION HAS CHANGED on github.com.
  • Port 22 blocked? ssh.github.com on port 443 — the hostname changes too, not just the port.
  • core.pager 'less -FX', git status -sb, git log --oneline, --no-edit, commit -m — the config that makes a 40-column screen workable.
  • SSH commit signing: Git 2.34+ with gpg.format ssh; GitHub verifies since 23 Aug 2022; ssh-keygen 8.8+ (8.7 is broken).
  • Never on a phone: git rebase -i, git push --force, git reset --hard, git clean -fdx. Use --force-with-lease if you must push over something.
  • On-device Git if you want it: Working Copy (iOS, $35.99 pro unlock for push and >5 repos, free for students), Termux (Android, free, real Git), a-Shell’s lg2 (iOS, minimal), iSH (slow).
Try TermAI

Free on iOS and Android. 5 AI requests/day on the free tier, plus unlimited SSH/SFTP and built-in Tailscale.

CC
Chen Chen — Founder of TermAI

Writes about mobile DevOps, terminal UX, and the surprising depth of "boring" infrastructure.