Stop Wrestling with GitLab! Soft Serve Is the SSH-First Server You Need
What if your Git server didn't need a browser? What if every repository, every commit, every line of code was accessible through the same terminal you already live in? Most developers have accepted a bizarre reality: we write code in sleek terminal editors like Neovim, orchestrate containers with CLI tools, deploy infrastructure with Terraform—but the moment we need to host our own Git repositories, we're forced back into bloated web interfaces, JavaScript↗ Bright Coding Blog-heavy dashboards, and endless configuration screens. GitLab wants 4GB of RAM just to boot. Gitea demands a database, a reverse proxy, and a weekend of tinkering. Even GitHub's enterprise offering feels like deploying a small city.
There's a better way. Hidden in plain sight, a tiny team of terminal obsessives has built something extraordinary. Soft Serve—the mighty, self-hostable Git server for the command line—flips the entire paradigm. No web UI. No browser tabs. Just pure, buttery-smooth SSH access with a gorgeous TUI that makes repository management feel like flying through your filesystem. Imagine cloning, browsing, and administering repositories without ever leaving your terminal. Imagine your Git server being so lightweight that it starts in seconds, not minutes. This isn't a fantasy. This is Soft Serve, and it's about to change how you think about code hosting forever.
What Is Soft Serve?
Soft Serve is a self-hostable Git server built by Charm, the legendary creators of Bubble Tea, Lipgloss, and the entire modern terminal UI ecosystem in Go. Born from a simple, radical idea—what if Git hosting was as elegant as the terminal itself—Soft Serve delivers a complete version control platform through SSH and HTTP protocols, wrapped in an interactive TUI that feels more like a native application than a server administration tool.
The project exploded onto the developer scene because it solves a genuine pain point that enterprise solutions ignore: developer experience at the CLI level. While GitLab and Gitea chase feature parity with GitHub's web interface, Soft Serve asks a contrarian question—why do we need that web interface at all? The result is a single binary called soft that runs everywhere: macOS, Linux, Windows, inside Docker↗ Bright Coding Blog containers, on Raspberry Pis, or massive cloud instances. No Node.js dependencies. No PostgreSQL↗ Bright Coding Blog requirement unless you want it. No webpack builds or asset pipelines.
Soft Serve is trending now because the developer tooling landscape is experiencing a terminal renaissance. Tools like LazyGit, fzf, and Charm's own ecosystem have proven that TUIs can outperform web apps for power users. Soft Serve extends this philosophy to infrastructure. It's not just a Git server; it's a statement about how developer tools should feel—fast, focused, and fundamentally keyboard-driven. The project has accumulated thousands of GitHub stars not through marketing, but through genuine word-of-mouth excitement from developers who tried it once and never looked back.
Key Features That Make Soft Serve Insane
SSH-First Architecture with Interactive TUI: Soft Serve's crown jewel is its terminal user interface accessible directly over SSH. Connect with ssh git.charm.sh and you're dropped into a navigable, keyboard-driven browser for repositories, commits, branches, and files. No curl commands. No JSON APIs. Just arrow keys, enter, and instant visual feedback.
Multi-Protocol Git Access: Clone and push via SSH, HTTP, or the native Git protocol. Soft Serve doesn't force you into one transport. The SSH port (:23231 by default) handles both interactive TUI sessions and Git operations. HTTP (:23232) provides fallback compatibility. The Git daemon (:9418) offers raw protocol access for legacy tooling.
Built-In Git LFS Support: Large file storage works out of the box with both HTTP and SSH backends. No plugins, no additional services, no configuration nightmares. The lfs config section lets you toggle SSH transfer optimization, but the HTTP path works immediately.
Zero-Configuration On-Demand Repo Creation: Push to a non-existent repository and Soft Serve creates it automatically. Or use the SSH CLI: ssh localhost repo create my-project. This eliminates the ceremony of web-based project creation—no forms, no templates, no waiting.
Granular Access Control with SSH Keys: Authentication is pure SSH public key cryptography. No passwords to manage, no OAuth flows to break. The anon-access setting controls unauthenticated permissions (from no-access through admin-access). Collaborators are added by public key. Private repositories hide from unauthorized eyes completely.
Syntax-Highlighted File Browsing Over SSH: Read code without cloning. ssh git.charm.sh repo blob soft-serve cmd/soft/main.go -c -l renders your file with syntax highlighting and line numbers directly in terminal. It's like cat evolved for the modern era.
Lightweight Single Binary Deployment: One executable. One data directory. Systemd service files included in official packages. The entire server starts with soft serve and consumes minimal resources compared to any containerized alternative.
Real-World Use Cases Where Soft Serve Dominates
Personal Dotfiles and Configuration Management: Stop paying GitHub for private repos to store your .vimrc and shell scripts. Run Soft Serve on a $5 VPS or your home server. Push dotfiles instantly with git push origin main. Browse configurations from any machine with SSH access. The TUI makes finding that obscure tmux setting you tweaked six months ago effortless.
Internal Team Git Hosting Without the Enterprise Tax: Small teams don't need GitLab's CI/CD pipelines, issue trackers, and wiki modules. They need fast, reliable Git hosting with sane access control. Soft Serve delivers exactly this. Set up in minutes, add team members by their SSH keys, create repositories on demand. Your infrastructure bill drops; your productivity doesn't.
Air-Gapped and Offline Development Environments: Organizations with strict security requirements often can't reach cloud Git providers. Soft Serve runs entirely self-contained. No external dependencies, no phoning home, no SaaS subscription to audit. Deploy behind your firewall, mirror critical repositories with repo import, and maintain complete operational independence.
Embedded Systems and Edge Computing: Need Git versioning on an IoT gateway, a factory floor controller, or a satellite? Soft Serve's minimal footprint makes it feasible where GitLab would be absurd. The SQLite default requires no separate database process. The binary cross-compiles to any Go-supported architecture.
Temporary Collaboration and Hackathons: Spin up Soft Serve for an event, share the SSH endpoint, and let participants push code immediately. Tear it down when done. No account creation flows, no permission matrices to configure. The on-demand repository creation means teams start coding in seconds, not after IT approval.
Step-by-Step Installation & Setup Guide
Getting Soft Serve running is deliberately minimal. Here's the complete path from zero to hosting.
Installation
Choose your preferred method from Charm's extensive packaging:
# macOS or Linux - Homebrew (recommended)
brew install charmbracelet/tap/soft-serve
# Windows with Winget
winget install charmbracelet.soft-serve
# Arch Linux
pacman -S soft-serve
# Nix
nix-env -iA nixpkgs.soft-serve
# Debian/Ubuntu - add Charm's official repository
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
# Fedora/RHEL
sudo tee /etc/yum.repos.d/charm.repo <<'EOF'
[charm]
name=Charm
baseurl=https://repo.charm.sh/yum/
enabled=1
gpgcheck=1
gpgkey=https://repo.charm.sh/yum/gpg.key
EOF
sudo yum install soft-serve
# Or install directly with Go
go install github.com/charmbracelet/soft-serve/cmd/soft@latest
Pre-built binaries for Linux, macOS, and Windows are also available on the releases page. Docker users can pull the official image.
First Server Launch
Before starting, ensure git is installed. Then:
# Set your SSH key as initial admin (critical for first access)
export SOFT_SERVE_INITIAL_ADMIN_KEYS="$(cat ~/.ssh/id_ed25519.pub)"
# Start the server
soft serve
This creates a data directory containing repositories, SSH host keys, and the SQLite database. The server immediately listens on three ports: 23231 (SSH/TUI), 23232 (HTTP), and 9418 (Git protocol).
Custom Data Location
For production deployments, relocate data outside the working directory:
# Run with persistent data path
SOFT_SERVE_DATA_PATH=/var/lib/soft-serve soft serve
Systemd Service
Production installations should use Systemd. Official packages include service units. Manual setup is documented in the systemd guide.
SSH Client Configuration
Simplify connections by adding to ~/.ssh/config:
Host soft
HostName localhost
Port 23231
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Now use ssh soft instead of the full command. Git recognizes this alias too:
git clone ssh://soft/my-new-project
REAL Code Examples from Soft Serve
Let's explore actual Soft Serve commands with deep technical breakdowns.
Example 1: Exploring the Public Demo Instance
Before installing, experience Soft Serve's capabilities through Charm's public server:
# Launch the interactive TUI directly into a specific repository
ssh git.charm.sh -t soft-serve
The -t flag forces pseudo-terminal allocation, essential for TUI rendering. Without it, SSH might suppress interactive features. This command demonstrates Soft Serve's core magic: repository browsing as a native terminal application, not a web page scraped into text.
For non-interactive file inspection:
# Print directory tree for the soft-serve repository
ssh git.charm.sh repo tree soft-serve
# Display a specific source file with full formatting
ssh git.charm.sh repo blob soft-serve cmd/soft/main.go
# Enhanced output: syntax highlighting (-c) plus line numbers (-l)
ssh git.charm.sh repo blob soft-serve cmd/soft/main.go -c -l
The repo tree and repo blob subcommands transform SSH from a shell protocol into a Git-specific query interface. The -c flag triggers syntax highlighting through Charm's internal rendering pipeline—detecting language from file extension and applying terminal color codes. The -l flag prepends line numbers, making this viable for quick code review without local clone overhead.
Example 2: Server Configuration (config.yaml)
After first launch, examine the generated configuration:
# Server identity displayed in TUI headers
name: "Soft Serve"
# Human-readable logs; switch to "json" for log aggregation pipelines
log_format: "text"
ssh:
listen_addr: ":23231" # SSH and TUI entry point
public_url: "ssh://localhost:23231" # Used in clone URLs shown to users
key_path: "ssh/soft_serve_host" # Host key for SSH handshake
client_key_path: "ssh/soft_serve_client" # Key for outbound SSH git operations
max_timeout: 0 # No hard limit on connection duration
idle_timeout: 120 # Drop stale connections after 2 minutes
git:
listen_addr: ":9418" # Native git:// protocol (read-only typically)
max_timeout: 0
idle_timeout: 3 # Aggressive cleanup for stateless git daemon
max_connections: 32 # Prevent resource exhaustion
http:
listen_addr: ":23232"
tls_key_path: "" # Empty = TLS disabled; set for HTTPS
tls_cert_path: ""
public_url: "http://localhost:23232"
cors: # Cross-origin rules for web tool integration
allowed_headers: ["Accept", "Authorization", "Content-Type", ...]
allowed_origins: ["http://localhost:23232"]
allowed_methods: ["GET", "HEAD", "POST", "PUT", "OPTIONS"]
db:
driver: "sqlite" # Zero-config default; "postgres" for scale
data_source: "soft-serve.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)"
lfs:
enabled: true # Git LFS active immediately
ssh_enabled: false # Pure-SSH LFS transfer; enable for performance
jobs:
mirror_pull: "@every 10m" # Cron syntax for mirror synchronization
stats:
listen_addr: ":23233" # Metrics endpoint for monitoring
This configuration reveals Soft Serve's architectural elegance. Each protocol (SSH, Git, HTTP) gets independent tuning. The public_url fields ensure generated clone URLs are externally resolvable even behind NAT or load balancers. Database pragmas enforce SQLite reliability under concurrent access. The stats server enables Prometheus-style scraping without bloating the core binary.
Example 3: User and Repository Management Over SSH
Administrative operations use the same SSH connection as everyday Git work:
# Create a new user with explicit public key
ssh -p 23231 localhost user create frankie '-k "ssh-ed25519 AAAATzN..."'
# Add additional keys to existing users
ssh -p 23231 localhost user add-pubkey frankie ssh-rsa AAAAB3Nz...
# Users self-manage their own keys
ssh -p 23231 localhost pubkey add ssh-ed25519 AAAA...
ssh -p 23231 localhost pubkey list
The user system is deceptively simple yet powerful. No passwords, no email verification, no forgotten credential flows. SSH public keys are the sole identity mechanism. The admin user created via SOFT_SERVE_INITIAL_ADMIN_KEYS holds god-mode privileges; subsequent users default to read-only on public repositories.
Repository lifecycle management:
# Explicit creation with metadata
ssh -p 23231 localhost repo create icecream \
'-d "Vanilla-flavored configuration templates"' \
'-n "Ice Cream Project"' \
-p # Private flag
# Or create implicitly by pushing
git remote add origin ssh://localhost:23231/charmbracelet/icecream
git push origin main # Repository materializes automatically
# Mirror external repositories for backup or caching
ssh -p 23231 localhost repo import soft-serve https://github.com/charmbracelet/soft-serve --mirror
# Fine-grained access control
ssh -p 23231 localhost repo collab add icecream beatrice read-only
ssh -p 23231 localhost repo collab add icecream frankie read-write
The repo import --mirror command establishes pull mirrors, synchronized by the cron job defined in config.yaml. Collaborator levels (no-access, read-only, read-write, admin-access) propagate across all access protocols—SSH, HTTP, and Git daemon.
Example 4: Access Token Generation for HTTP Authentication
When SSH key authentication isn't viable for HTTP clients:
# Create a long-lived token
ssh -p 23231 localhost token create 'CI/CD deployment token'
# Output: ss_1234abc56789012345678901234de246d798fghi
# Or limit exposure with expiration
ssh -p 23231 localhost token create --expires-in 1y 'Annual rotation token'
# Output: ss_98fghi1234abc56789012345678901234de246d7
# Use as HTTP basic auth username (password can be empty)
git clone http://ss_98fghi1234abc56789012345678901234de246d7@localhost:23232/private-repo.git
Tokens bridge the SSH-centric design to HTTP workflows. The ss_ prefix enables quick log analysis. Expiration support enforces security hygiene without manual revocation campaigns.
Advanced Usage & Best Practices
Hook-Driven Automation: Soft Serve's server-side hooks (pre-receive, update, post-update, post-receive) enable CI/CD triggers without external dependencies. Place global hooks in <data path>/hooks/ for organization-wide policies, or per-repository hooks for project-specific workflows. The example update hook in the documentation demonstrates push notification formatting—adapt this to trigger webhook calls, update issue trackers, or notify chat channels.
Database Scaling Strategy: Start with SQLite for simplicity. When horizontal scaling or high-availability becomes necessary, migrate to PostgreSQL without application changes:
# Create database
psql -h db.internal -U admin -c 'CREATE DATABASE soft_serve'
# Launch with Postgres
SOFT_SERVE_DB_DRIVER=postgres \
SOFT_SERVE_DB_DATA_SOURCE="postgres://soft_user:secure_pass@db.internal:5432/soft_serve?sslmode=require" \
soft serve
Security Hardening: Disable anonymous access for private installations:
ssh soft settings allow-keyless false
ssh soft settings anon-access no-access
This forces all connections through SSH key authentication. Combine with IdentitiesOnly yes in client SSH config to prevent key confusion attacks.
Terminal Clipboard Integration: Soft Serve's TUI supports OSC52 for copying clone commands directly to your local clipboard over SSH. This requires terminal emulator support (iTerm2, WezTerm, modern Windows Terminal). Press c on highlighted repositories to test—it's magical when it works.
Comparison with Alternatives
| Feature | Soft Serve | Gitea | GitLab CE | Bare Git + SSH |
|---|---|---|---|---|
| Deployment Complexity | Single binary | Binary + database + config | Complex omnibus/Docker | Manual setup |
| Resource Usage | Minimal | Moderate | Heavy (4GB+ RAM) | Minimal |
| Web Interface | None (TUI over SSH) | Full web UI | Full web UI + CI/CD | None |
| Repository Creation | Push-to-create or SSH CLI | Web form or API | Web form or API | Manual git init --bare |
| Access Control | SSH keys + tokens | Built-in auth + OAuth | LDAP, SAML, OAuth | Manual .ssh/authorized_keys |
| Git LFS | Built-in | Plugin | Built-in | Manual server |
| Syntax Highlighting | Terminal-native | Web-based | Web-based | None |
| Ideal For | CLI purists, minimalists | Small teams wanting GitHub-like | Enterprises needing full DevOps↗ Bright Coding Blog | Masochists |
Soft Serve occupies a unique position: more accessible than raw Git hosting, more focused than Gitea, infinitely lighter than GitLab. Choose it when terminal velocity matters more than feature checklists.
FAQ
Is Soft Serve production-ready? Absolutely. Charm uses it internally. The project has stable releases, automated builds, and active maintenance. Start with SQLite; scale to PostgreSQL when needed.
Can I use Soft Serve without SSH keys?
Technically yes, but discouraged. The allow-keyless setting permits anonymous access, and HTTP tokens provide alternative authentication. However, SSH keys unlock the full TUI experience and represent the intended workflow.
Does Soft Serve support pull requests or code review? Not natively. Soft Serve deliberately excludes these features to maintain simplicity. Use it alongside tools like Delta for diff review, or integrate hooks with external systems. It's a Git server, not a project management platform.
How do I backup my Soft Serve instance?
The data directory is self-contained. Archive it with standard tools: tar czf soft-serve-backup.tar.gz /var/lib/soft-serve. For PostgreSQL deployments, include database dumps in your backup strategy.
Why no RSA key support?
Go's x/crypto/ssh package lacks modern RSA algorithm support. Use Ed25519 keys— they're more secure and shorter anyway. Generate with ssh-keygen -t ed25519 if needed.
Can I run Soft Serve behind a reverse proxy?
Yes. Configure public_url in config.yaml to reflect your external address. For SSH, standard port forwarding or TCP load balancing works. For HTTP, any reverse proxy (Nginx, Caddy, Traefik) handles TLS termination before Soft Serve's HTTP port.
Is there a web UI planned? Unlikely. The terminal-first philosophy is core to Soft Serve's identity. The Charm ecosystem builds tools for developers who live in the terminal, not escape from it.
Conclusion
Soft Serve isn't just another Git server—it's a philosophical reset. In an industry obsessed with adding features until products collapse under their own weight, Charm had the courage to ask what could be removed. The answer: everything that isn't pure Git hosting, wrapped in an interface that respects where developers actually work.
The result is intoxicatingly fast. No Docker Compose files to debug. No JavaScript bundles to cache-bust. No database migrations to fear. Just soft serve and you're hosting repositories with a TUI that makes browsing code feel like navigating your own filesystem.
Does it replace GitLab for enterprises needing integrated CI/CD and issue tracking? No. That's not the point. Soft Serve claims a different territory: the space where simplicity, speed, and terminal-native design create an experience so fluid that going back to web dashboards feels like downgrading from a sports car to a bus.
Your move. Stop accepting bloated infrastructure as inevitable. Clone the repository, install the binary, or just try the public demo with ssh git.charm.sh. Feel what Git hosting should have been all along. Then star the project, share it with your terminal-obsessed friends, and join the growing community of developers who've discovered that sometimes, the best interface is no interface at all—just you, your keyboard, and your code.
👉 Get Soft Serve on GitHub — because your Git server deserves to be as elegant as the code you write.