Why Leave GitHub?
GitHub is the default for good reasons: it's free, universally supported, and every tool in the ecosystem talks to it natively. But when your AI agents run on a private LAN โ no internet access by design โ GitHub becomes a liability. Every git push, every git clone, every webhook is a network call that either fails silently or requires punching a hole in your isolation boundary.
Beyond the connectivity issue, there's a simpler principle at work: for a local AI agent stack, your git server is infrastructure, not a product. You don't need project management features, CI/CD pipelines, or a social graph. You need a place for agents to push and pull code reliably, with zero maintenance overhead. The complexity that makes GitHub great for teams of humans becomes friction when the primary consumers are automated agents.
This post breaks down every viable option, with specific attention to how well each one serves AI agent workflows on an isolated network.
What Does an AI Agent Actually Need from Git?
Before evaluating options, it helps to be precise about the requirements. An AI agent doing code work needs:
- SSH or HTTP clone/push/pull โ the same git transport any tool uses
- No manual repo creation friction โ agents should be able to push to a new repo without a human pre-creating it
- Key-based auth โ agents authenticate with SSH keys, no password prompts
- Reliable availability โ the server needs to stay up with no babysitting
- API access (optional but useful) โ a REST or GraphQL API lets agents list repos, check status, create branches programmatically
What agents do not need: issue trackers, pull request UI, CI/CD runners, user management for dozens of accounts, or web-based code review. Optimizing for human features in a primarily agent-driven system adds complexity with no return.
Option 1: Bare Git over SSH
The absolute floor of git servers: a unix user account, an SSH daemon, and bare git repositories in a directory. Nothing else. Any machine running sshd and git already qualifies.
How It Works
Create a git user, add authorized SSH public keys (for each agent + human admin), and initialize bare repos:
# On the server sudo adduser git sudo su - git mkdir repos && cd repos git init --bare myproject.git
Clients push/pull with: git clone git@server:repos/myproject.git
Pros
- Zero dependencies beyond git and sshd โ both are already on every Linux machine
- Zero processes to monitor or restart
- Completely transparent โ no magic, no database, no framework
- Survives indefinitely without updates
Cons
- Every new repository must be manually
git init --bare'd on the server before anyone can push to it โ agents can't create repos on demand - No API โ agents can't query available repos programmatically
- Access control is all-or-nothing per SSH key unless you layer on something like gitolite
- No web browsability at all
Verdict
Right choice if you have a small, fixed set of repos that change infrequently, one or two human admins, and zero tolerance for any additional process. Wrong choice if agents need to create new repos dynamically or if you want any kind of repo discovery.
Option 2: Soft Serve
Soft Serve by Charmbracelet is a single-binary, SSH-first git server built for terminal-native workflows. It's the most elegant minimal option โ considerably more capable than bare git over SSH, with nearly as little operational overhead.
Key Features
- Single binary โ one file, no database required (SQLite embedded), no runtime dependencies beyond git
- SSH TUI โ browse repos, files, and commit history over SSH:
ssh yourserver -t - Repos created on first push โ
git pushto a nonexistent repo creates it automatically. This is the critical feature for agent workflows. - SSH key-based auth and access control โ per-repo public/private visibility, per-key permissions
- HTTP clone support โ agents that prefer HTTP over SSH can clone via HTTP too
- Git LFS support โ handles large file storage if agents are working with models or binary assets
- User access tokens โ agents can authenticate with tokens instead of raw SSH keys if preferred
- REST API โ Soft Serve exposes an HTTP API for repo management, listing repos, and basic admin operations
- Systemd service included โ run it as a managed service with one unit file
git push ssh://server/new-experiment-7 for the first time just works. This alone makes it significantly better than bare git for autonomous agent use.
Resource Usage
~5โ15MB RAM at idle. Negligible CPU. Runs fine on a Raspberry Pi. The SQLite database stays under a few MB for hundreds of repos.
Cons
- No web UI (the SSH TUI is the UI โ fine for terminal users, not browser-accessible)
- No issues, PRs, or project management (not a problem for agent-primary workflows)
- Smaller community than Gitea/Forgejo; fewer integrations
- REST API is functional but less comprehensive than Gitea's
Option 3: Forgejo
Forgejo is a community-governed fork of Gitea โ the lightweight GitHub clone written in Go. If Soft Serve is a sharp knife, Forgejo is a full kitchen. It does everything: web UI, issues, pull requests, webhooks, Gitea Actions (CI/CD), user management, organization support, package registry, and a comprehensive REST API that GitHub-compatible tools can talk to natively.
Why Forgejo over Gitea
Gitea is backed by Gitea Ltd., which has moved some features toward a cloud-hosted paid product. Forgejo is a non-profit fork governed by the Forgejo community with a commitment to 100% free software. Feature-for-feature they're nearly identical today; the difference is governance. For a self-hosted setup with no commercial interest, Forgejo's model is cleaner.
Key Features for Agent Workflows
- Full GitHub-compatible REST API โ tools that know how to talk to GitHub (including many AI coding tools) can point at Forgejo with minimal reconfiguration
- Webhooks โ agents can trigger on push events, PRs, comments
- SSH + HTTP clone โ full standard git transport
- Forgejo Actions โ GitHub Actions-compatible CI runner, if you want agents to trigger automated pipelines
- Web UI โ useful for humans reviewing agent-generated code
Resource Usage
~100โ150MB RAM idle in Docker. Lightweight enough for a Raspberry Pi 4 (with some patience). A dedicated machine or your always-on GPU rig handles it trivially.
Cons
- More moving parts: Docker container (or binary + service), database (SQLite default, Postgres for larger), optional reverse proxy
- More surface area for configuration, though defaults are solid
- The feature richness you don't need still needs to be maintained (updates, security patches)
Option 4: Gitolite
Gitolite is a Perl-based access control layer that sits on top of bare git over SSH. It manages per-repo, per-branch, per-user permissions through a configuration file stored in a git repo itself โ meaning access control changes are made by committing to a config repo. It's powerful, battle-tested, and used by large organizations to manage hundreds of repos and users.
For an isolated local AI setup, gitolite adds complexity without much return. The access control features it provides are unnecessary on a trusted private network. The configuration model (a git repo that controls git repos) is elegant but has a learning curve. It has no web UI, no API, and no auto-create on push. Avoid it unless you have a specific need for granular per-branch ACLs.
What to Skip
GitLab CE
GitLab Community Edition is the most feature-complete self-hosted option โ and the most demanding. It requires a minimum of 4GB RAM just to run, often needs 8GB+ for reasonable performance, and has a complex multi-process architecture (Rails, Sidekiq, Puma, Nginx, Redis, Postgres, Gitaly). For a local AI lab, this is a data center product being asked to do a simple job. The maintenance burden alone disqualifies it.
Gogs
Gogs is the original lightweight Go git server that inspired Gitea. It's less actively maintained now and has fallen behind both Gitea and Forgejo in features and security patches. No compelling reason to choose it over Forgejo today.
Ollama-style "just use the hosted thing"
If you're running a completely air-gapped network, GitHub isn't an option by definition. But it's worth noting that if your isolation is partial (no sensitive data but LAN-only preference), GitHub remains a zero-maintenance option for the git layer and may be worth keeping for non-sensitive repos while running a local server for sensitive ones.
The Verdict: Two Tiers
| Option | Install | RAM | API | Auto-create repos | Web UI | Best for |
|---|---|---|---|---|---|---|
| Soft Serve | Single binary | ~10MB | REST (basic) | โ Yes | SSH TUI only | Agent-primary, terminal teams |
| Forgejo | Docker Compose | ~120MB | REST (GitHub-compatible) | โ Yes | โ Full | Human + agent mixed workflows |
| Bare Git + SSH | Nothing | 0MB | None | โ Manual | None | Fixed repo sets, one admin |
| Gitolite | Perl + config | ~5MB | None | โ Manual | None | Complex per-branch ACLs |
| GitLab CE | Complex multi-process | 4โ8GB+ | REST (full) | โ Yes | โ Full | Enterprise teams |
For a local AI agent setup on an isolated network, the answer is one of two things depending on your needs:
- Soft Serve โ if your agents are the primary consumers, you live in the terminal, and you want the absolute minimum footprint. Single binary, auto-creates repos on push, SSH TUI, REST API for programmatic access. Install it in 5 minutes and forget it exists.
- Forgejo โ if you also need humans to review agent code in a browser, want GitHub-compatible tooling to work out of the box, or want CI/CD pipelines triggered by agent pushes. Still lightweight enough to run on a Pi. Docker Compose deploy in 10 minutes.
Setup: Soft Serve in 5 Minutes
# Install (Debian/Ubuntu) sudo mkdir -p /etc/apt/keyrings curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor \ -o /etc/apt/keyrings/charm.gpg echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" \ | sudo tee /etc/apt/sources.list.d/charm.list sudo apt update && sudo apt install soft-serve # Set your admin SSH key and start export SOFT_SERVE_INITIAL_ADMIN_KEYS="$(cat ~/.ssh/id_ed25519.pub)" export SOFT_SERVE_DATA_PATH=/var/lib/soft-serve soft serve & # Browse your repos from any machine on the LAN ssh yourserver -p 23231 # Clone a repo (creates it on first push) git clone ssh://yourserver:23231/myproject git remote add origin ssh://yourserver:23231/new-agent-project git push origin main # repo created automatically
To run as a persistent service:
# /etc/systemd/system/soft-serve.service [Unit] Description=Soft Serve Git Server After=network-online.target [Service] Type=simple Restart=always ExecStart=/usr/bin/soft serve Environment=SOFT_SERVE_DATA_PATH=/var/lib/soft-serve EnvironmentFile=-/etc/soft-serve.conf [Install] WantedBy=multi-user.target
sudo systemctl enable --now soft-serve
Add an AI agent's SSH public key:
# Via SSH command (as admin) ssh yourserver -p 23231 user create agent-codex --admin=false ssh yourserver -p 23231 user add-pubkey agent-codex "ssh-ed25519 AAAA..."
Setup: Forgejo in 10 Minutes (Docker Compose)
# docker-compose.yml
services:
forgejo:
image: codeberg.org/forgejo/forgejo:latest
container_name: forgejo
restart: always
ports:
- "3000:3000" # Web UI (optional, skip if agents-only)
- "2222:22" # SSH
volumes:
- ./data:/data
environment:
- USER_UID=1000
- USER_GID=1000
- FORGEJO__server__DOMAIN=gitserver.local
- FORGEJO__server__SSH_DOMAIN=gitserver.local
- FORGEJO__server__SSH_PORT=2222
- FORGEJO__database__DB_TYPE=sqlite3
docker compose up -d # First run: visit http://yourserver:3000 and complete setup wizard # Or configure headlessly via app.ini in ./data/gitea/conf/
Agent SSH clone pattern:
git clone ssh://git@yourserver:2222/agent-user/repo.git
Use Forgejo's API from an agent:
# List all repos
curl http://yourserver:3000/api/v1/repos/search \
-H "Authorization: token YOUR_API_TOKEN"
# Create a new repo
curl -X POST http://yourserver:3000/api/v1/user/repos \
-H "Authorization: token YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"new-experiment","auto_init":true}'
Security on an Isolated Network: What You Can Relax
Standard git server security guidance is written for internet-facing deployments where the threat model includes external attackers, credential brute force, and supply chain attacks. On a completely isolated LAN, most of that threat model doesn't apply.
What you can reasonably relax:
- HTTPS/TLS โ HTTP over LAN is fine. No TLS certificate management needed.
- Rate limiting and fail2ban โ only relevant if untrusted clients can reach the server
- Two-factor authentication โ add operational friction with no security benefit on a trusted LAN
- Regular security update urgency โ still update occasionally, but not emergency-patch cadence
- Complex access control โ if all machines on the LAN are trusted, a single shared SSH key for all agents is fine
What still matters even on an isolated network:
- Backups โ your repos are valuable even without an external attacker. Back up the data directory.
- SSH key management โ still use per-agent keys so you can revoke one without replacing all of them
- Basic availability โ run as a systemd service or Docker container that auto-restarts
rsync -av /var/lib/soft-serve/ backup-server:/backups/soft-serve/ on a cron. For Forgejo: same with ./data/. SQLite databases are single files โ trivial to snapshot. No backup tooling required.
References
- Charmbracelet โ Soft Serve GitHub Repository โ The official source for Soft Serve documentation, installation, and features. โ github.com/charmbracelet/soft-serve
- Forgejo โ Official Site and Documentation โ Community-governed Gitea fork. Installation guides, API docs, and comparison with upstream. โ forgejo.org
- Charm.land โ Self-Hosted Soft Serve Guide โ Official guide to running Soft Serve with HTTPS and SSH on a self-hosted server. โ charm.land
- Soft Serve Systemd Integration โ Official service unit file and configuration documentation for running Soft Serve as a managed system service. โ github.com/charmbracelet/soft-serve/blob/main/systemd.md