While “the future of large files in git is git”,
the present of large files in git is still something like git-lfs.
One of git’s selling points to me is its distributed nature,
and the ease of setting up a simple server with sshd.
I am happy to pay GitHub to handle hosting and CI because I’m not locked in there —
I can and have switched repos away to personal hosting and other forges and then back again to GitHub.
But git-lfs breaks this: one you have files tracked in LFS,
you can only push to other git servers that provide LFS,
which for self-hosting means something more complicated than a simple SSH server.
Or so I thought, until I discovered git-lfs-transfer,
which can be installed on the server to provide git LFS hosting over ssh.
This program has two implementations I’m aware of:
- bk2204/scutiger, in Rust, is the original and reference implementation
- charmbracelet/git-lfs-transfer, is a Go port
I found these today when I needed to work with an LFS-enabled repository in my Claude Code prison. This VM happened to have Go tooling already installed, so I used the charmbracelet port.
I ran this on the server:
sudo dnf install git-lfs
go install github.com/charmbracelet/git-lfs-transfer@latest
sudo cp ~/Documents/Go/bin/git-lfs-transfer /usr/local/bin/
sudo chmod 755 /usr/local/bin/git-lfs-transfer
Then on the client I could:
# Using ssh:// URLs may be a requirement, see below
git remote add REMOTE ssh://USER@HOST/path/to/repo.git
# Yes, the https:// is reequired for this, probably a holdover from previous HTTP-only LFS implementation
git config lfs.https://HOST/path/to/repo.git/info/lfs.locksverify false
After that, pushing my LFS-enabled git checkout to my server worked perfectly from the client. No HTTP required!
Troubleshooting & references
- This requires
git-lfs3.0 or newer - Git over ssh does not execute your shell before running commands,
so any
$PATHconfiguration that includes Go or Rust bin directories doesn’t get applied. For me this just included/usr/binand/usr/local/bin. See it withssh USER@HOST 'echo $PATH'. - If you see an error message related to a missing
git-lfs-authenticatebinary, that means that usinggit-lfs-transferhas failed for some reason.git-lfs-authenticateis the normal way that forges enable pulling large files from their HTTP servers, and because it’s forge-specific, there is no generic implementation shipped withgit-lfs. Ifgit-lfs-transferfails (or is not in the$PATH, or yourgit-lfsversion is too old, etc), it will fall back togit-lfs-authenticate. (Some details in this comment.) - It may
be necessary to use
ssh://USER@HOST/path/to/repo.gitremote URIs rather thanUSER@HOST:/path/to/repo.gitstyle. - As far as I know, the two
git-lfs-transferimplementations don’t have a way to support LFS locks. LFS locks are an optional coordination feature for preventing simultaneous edits of the same file. If you don’t explicitly disable it as shown above, git will warn you on every push that it isn’t supported. git-lfshas a little hack for pushing to repos on the local filesystem, sogit push /path/to/wherever.gitshould work just fine even if your working copy is LFS-enabled