Git stores every version of every file forever. That’s perfect for code, but painful for large binary files (images, videos, design files, game assets, datasets, compiled artifacts): the repository grows huge, and every clone downloads all versions. Git LFS (Large File Storage) replaces large files in the repository with small pointer files, and stores the actual content on an LFS server, downloading only the versions you need.

How it works

  • You tell LFS which file patterns to track (*.psd, *.mp4).
  • On commit, the file content goes to LFS storage. Git stores a tiny pointer:
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a2146…
size 52428800
  • On checkout, LFS downloads the content for the commit you check out.

Install

sudo apt install git-lfs          # Debian/Ubuntu
git lfs install                   # once per user: sets up Git hooks/filters

Track files

git lfs track "*.psd" "*.mp4" "assets/models/**"
cat .gitattributes
# *.psd filter=lfs diff=lfs merge=lfs -text
git add .gitattributes
git add design/logo.psd
git commit -m "Add logo source via LFS"
git push

Commit .gitattributes. That’s how everyone’s Git knows which files belong in LFS.

git lfs ls-files          # files stored in LFS
git lfs status

Migrate files already in history

If large files were committed normally, they stay in history and keep the repository big. git lfs migrate rewrites history to move them into LFS:

git lfs migrate info --everything --top=10       # find the biggest file types
git lfs migrate import --include="*.psd,*.mp4" --everything
git push --force-with-lease --all

This rewrites history: coordinate with your team, because everyone must re-clone or reset. Back up the repository first.

LFS on your own Git server

  • Gitea / Forgejo: LFS is supported. Enable it with LFS_START_SERVER = true in app.ini (enabled by default in recent versions), and set the storage path or S3/MinIO storage. See Gitea and MinIO.
  • GitLab: LFS is enabled by default, with local or object storage. See install GitLab.
  • Raise reverse proxy upload limits for big files (client_max_body_size in Nginx). See Nginx 413 errors.

LFS objects can use a lot of disk. Plan storage accordingly, or offload them to object storage. See storage VPS.

Hosted service limits

GitHub, GitLab.com and others limit LFS storage and bandwidth per account, with paid packs for more. CI pipelines that clone with LFS on every run consume bandwidth quickly. Cache LFS objects in CI, or skip them when they’re not needed:

GIT_LFS_SKIP_SMUDGE=1 git clone https://git.example.com/team/repo.git   # pointers only
git lfs pull --include="assets/needed/**"                                # fetch selectively

Pitfalls

  • Forgetting git lfs install on a new machine: files check out as pointer text files.
  • Tracking patterns after committing files: existing files stay in normal Git, unless migrated.
  • Binary merges: LFS doesn’t make binary files mergeable. Use git lfs lock for files only one person should edit at a time (supported by many servers).
  • Backups: back up LFS storage along with repositories. A repository backup without LFS objects has only pointers. See backup strategy.

Alternatives

For very large datasets or build artifacts, LFS may not be the right tool: consider DVC (data version control), object storage with versioning, or an artifact registry.

Quick reference

Task Command
Set up git lfs install
Track git lfs track "*.psd" + commit .gitattributes
List LFS files git lfs ls-files
Move history into LFS git lfs migrate import --include="…" --everything
Clone without content GIT_LFS_SKIP_SMUDGE=1 git clone …
Fetch content git lfs pull

Related: Git submodules and .gitignore guide.

  • Git
  • Storage
  • DevOps