PromptHub
Back to Blog
Developer Tools DevOps

Stop Wrestling with GitLab! Soft Serve Is the SSH-First Server You Need

B

Bright Coding

Author

6 min read 16 views
Stop Wrestling with GitLab! Soft Serve Is the SSH-First Server You Need

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.

Comments (0)

Comments are moderated before appearing.

No comments yet. Be the first to share your thoughts!

Recommended Prompts

View All
All tools