This guide covers migrating from the legacy bash/Python implementation to the new Go-based bosun CLI.
Bosun was rewritten in Go for several reasons:
- Single binary distribution - No Python, uv, or bash dependencies
- Better testing - Unit tests and golden file tests
- Type safety - Catch errors at compile time
- Native Docker SDK - First-party Docker integration
- Concurrency - Goroutines for watch mode and parallel operations
See ADR-0010: Go Rewrite for the full decision rationale.
| Old (Bash) | New (Go) | Notes |
|---|---|---|
bin/bosun yacht up |
bosun yacht up |
Same syntax |
bin/bosun crew list |
bosun crew list |
Same syntax |
cd manifest && uv run manifest.py render |
bosun provision |
Integrated |
cd manifest && uv run manifest.py provisions |
bosun provisions |
Integrated |
| Manual reconcile.sh | bosun reconcile |
Integrated |
Before (Bash/Python):
bin/
bosun # 1400-line bash script
manifest/
manifest.py # Python renderer
pyproject.toml # Python dependencies
bosun/
scripts/
reconcile.sh # GitOps workflow
entrypoint.sh
healthcheck.sh
After (Go):
cmd/bosun/
main.go # Entry point
internal/
cmd/ # Cobra commands
manifest/ # YAML rendering (ported from Python)
docker/ # Docker SDK wrapper
reconcile/ # GitOps engine (ported from bash)
snapshot/ # Rollback system
ui/ # Colored output
build/
bosun # Compiled binary
-
No Python dependency - The
uv run manifest.pycommand is replaced bybosun provision -
Config file location - Bosun now looks for config in:
./bosun.yaml./.bosun.yaml$HOME/.config/bosun/config.yaml
-
Environment variables - Some variable names standardized:
REPO_URL(unchanged)REPO_BRANCH(unchanged)DEPLOY_TARGET(previouslyTARGET_HOSTin some contexts)
-
Manifest syntax - Unchanged. The Go version reads the same YAML manifests.
cd /path/to/bosun
make buildThis creates ./build/bosun.
Test that the new binary works correctly:
# Check version
./build/bosun --version
# Run doctor to verify environment
./build/bosun doctor
# Test manifest rendering (dry-run)
./build/bosun provision core --dry-run
# Compare output if neededOption A - Add build directory to PATH:
export PATH="$PATH:/path/to/bosun/build"Option B - Create alias:
alias bosun='/path/to/bosun/build/bosun'Option C - Install to GOPATH:
make installThe legacy bash/Python files may have already been removed. If they still exist, remove them:
# Check if legacy files exist
ls -la bin/bosun manifest/manifest.py manifest/pyproject.toml 2>/dev/null
# If they exist, create backup first (optional)
mkdir -p legacy-backup
cp bin/bosun legacy-backup/ 2>/dev/null
cp manifest/manifest.py manifest/pyproject.toml legacy-backup/ 2>/dev/null
# Remove legacy files
rm -f bin/bosun
rm -f manifest/manifest.py
rm -f manifest/pyproject.toml
# Remove empty directories if applicable
rmdir bin 2>/dev/null || trueFiles to remove:
| File | Description |
|---|---|
bin/bosun |
Original bash script (~1400 lines) |
manifest/manifest.py |
Python YAML renderer (~330 lines) |
manifest/pyproject.toml |
Python dependencies |
Run these commands to verify the migration worked:
# 1. Check version
bosun --version
# Expected: bosun version 0.2.0
# 2. Run doctor
bosun doctor
# Expected: All checks pass (or show expected warnings)
# 3. List containers
bosun crew list
# Expected: Shows running containers
# 4. Check yacht status
bosun yacht status
# Expected: Shows compose services
# 5. Test manifest rendering
bosun provision core --dry-run
# Expected: YAML output matching previous behaviorThe binary isn't in your PATH. Either:
- Run with full path:
./build/bosun - Add to PATH:
export PATH="$PATH:$(pwd)/build" - Install:
make install
Create a bosun.yaml in your project root:
root: .
manifest_dir: manifestEnsure Docker is running:
docker psThe Go version should produce identical output. If you see differences:
- Run both versions with
--dry-run - Compare outputs with
diff - Report any discrepancies as bugs
If you need to rollback to the bash version:
# Restore from backup
cp legacy-backup/bosun bin/
cp legacy-backup/manifest.py legacy-backup/pyproject.toml manifest/
# Or restore from git
git checkout HEAD -- bin/bosun manifest/manifest.py manifest/pyproject.tomlWhat changed: Template rendering has been migrated from chezmoi to native Go text/template with Sprig functions.
Why: Single binary distribution - no external chezmoi binary required. Faster execution and better security (secrets processed entirely in-memory).
Impact on templates:
- Syntax unchanged - Go template syntax (
{{ .value }},{{ if }}, etc.) works identically - New functions available - All Sprig functions are now available
- No breaking changes - Existing templates continue to work
Benefits:
- No external binary dependency
- Secrets never exposed via environment variables
- Faster template processing
- Smaller container images
What changed: Manifests now support explicit schema versioning with apiVersion and kind fields.
Example manifest format:
apiVersion: bosun.io/v1
kind: Service
name: myapp
provisions:
- container
- reverse-proxy
config:
image: ghcr.io/org/myapp:latest
port: 3000Supported kinds:
| Kind | Description |
|---|---|
Provision |
Reusable provision template |
Stack |
Collection of services |
Service |
Individual service definition |
Migration command:
# Migrate unversioned manifests to the new format
bosun migrate
# Preview changes without modifying files
bosun migrate --dry-runBackwards compatibility:
- Manifests without
apiVersioncontinue to work with a warning - The
bosun migratecommand adds versioning to existing manifests - No breaking changes to existing workflows
- Run
bosun --helpfor command documentation - See docs/commands.md for full reference
- Check docs/adr/0010-go-rewrite.md for design decisions