GitOps for Docker Compose. No Kubernetes required.
Push to git. Bosun receives orders. Containers deploy. Smooth sailing.
┌──────────┐ ┌───────────────────────┐ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────────┐ ┌────────────────────┐
│ │ │ │ │ │ │ │ │ │ │ │ │ │
│ git push ├────►│ Bosun receives orders ├────►│ Clone & decrypt ├────►│ Template configs ├────►│ Deploy to target ├────►│ docker compose up ├────►│ Drift verification │
│ │ │ │ │ │ │ │ │ │ │ │ │ │
└──────────┘ └───────────────────────┘ └─────────────────┘ └──────────────────┘ └──────────────────┘ └───────────────────┘ └────────────────────┘
You run 40 containers on bare metal. Traefik routes traffic. Secrets are everywhere. You want GitOps -- push a change, everything updates -- but Kubernetes is overkill for a homelab.
Bosun is Helm for home: a single binary that brings GitOps workflows to Docker Compose.
| What you get | How it works |
|---|---|
| Push-to-deploy | Webhooks or polling trigger reconciliation |
| Secret management | SOPS + Age encryption, decrypted at deploy time |
| Config templating | Go templates + Sprig functions, DRY service definitions |
| Drift detection | Periodic checks: is what's running what you declared? |
| Multi-provider alerts | Discord, SendGrid, Twilio notifications on deploy events |
| Single binary | No Python, no Node, no bash scripts on target |
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Your Yacht (Server) │
│ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ Bosun │ │
│ │ │ │
│ │ │ │
│ │ ┌────────────────────────────────────┐ ┌──────────────────────────────┐ │ │
│ │ │ │ │ │ │ │
│ │ │ git push │ ┌┄┄┤ Drift Watch / Periodic check │ │ │
│ │ │ │ ┆ │ │ │ │
│ │ └──────────────────┬─────────────────┘ ┆ └──────────────────────────────┘ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ ▼ ┆ │ │
│ │ ┌────────────────────────────────────┐ ┆ │ │
│ │ │ │ ┆ │ │
│ │ │ Radio / Webhook/Poll │ ┆ │ │
│ │ │ │ ┆ │ │
│ │ └──────────────────┬─────────────────┘ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ ▼ ┆ │ │
│ │ ┌────────────────────────────────────┐ ┆ │ │
│ │ │ │ ┆ │ │
│ │ │ Fetch Orders / git clone/pull │ ┆ │ │
│ │ │ │ ┆ │ │
│ │ └──────────────────┬─────────────────┘ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ ▼ ┆ │ │
│ │ ┌────────────────────────────────────┐ ┆ │ │
│ │ │ │ ┆ │ │
│ │ │ Decrypt Secrets / SOPS + Age │ ┆ │ │
│ │ │ │ ┆ │ │
│ │ └──────────────────┬─────────────────┘ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ ▼ ┆ │ │
│ │ ┌────────────────────────────────────┐ ┆ │ │
│ │ │ │ ┆ │ │
│ │ │ Prep Configs / Go Templates │ ┆ │ │
│ │ │ │ ┆ │ │
│ │ └──────────────────┬─────────────────┘ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ ▼ ┆ │ │
│ │ ┌────────────────────────────────────┐ ┆ │ │
│ │ │ │ ┆ │ │
│ │ │ Deploy / tar-over-SSH / local copy │ ┆ │ │
│ │ │ │ ┆ │ │
│ │ └──────────────────┬─────────────────┘ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ │ ┆ │ │
│ │ ▼ ┆ │ │
│ │ ┌────────────────────────────────────┐ ┆ │ │
│ │ │ │ ┆ │ │
│ │ │ Crew Up / docker compose │ ┆ │ │
│ │ │ │ ┆ │ │
│ │ └──────────────────┬─────────────────┘ ┆ │ │
│ │ │ ┆ │ │
│ └────────────────────┼────────────────────┆───────────────────────────────────┘ │
│ verify┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘ │
│ ┆ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ │ │
│ │ Your Crew / Containers │ │
│ │ │ │
│ └────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
curl -fsSL https://raw.githubusercontent.com/cameronsjo/bosun/main/scripts/install.sh | bashDownloads the latest release, verifies the SHA256 checksum, and installs to /usr/local/bin.
# Go install
go install github.com/cameronsjo/bosun/cmd/bosun@latest
# From source
git clone https://github.com/cameronsjo/bosun.git
cd bosun && make build
./build/bosun --versionbosun update # Download and install latest
bosun update --check # Check without installingReleases are signed with Cosign and include SLSA provenance.
# Verify checksum signature
cosign verify-blob --certificate checksums.txt.pem \
--signature checksums.txt.sig checksums.txt
# Verify build provenance
gh attestation verify bosun_*.tar.gz --owner cameronsjo# 1. Generate encryption key
age-keygen -o ~/.config/sops/age/keys.txt
# 2. Create .sops.yaml with your public key
cat > .sops.yaml << 'EOF'
creation_rules:
- path_regex: .*\.yaml$
age: <your-public-key>
EOF
# 3. Initialize your yacht
bosun init
# 4. Check if everything is seaworthy
bosun doctor
# 5. Start the yacht
bosun yacht up| Command | Description |
|---|---|
bosun init |
Interactive setup wizard (--systemd for unit files) |
bosun doctor |
Pre-flight checks |
bosun validate |
Validate config and daemon connectivity |
bosun status |
Health dashboard |
| Command | Description |
|---|---|
bosun daemon |
Run the GitOps daemon |
bosun reconcile |
One-shot GitOps workflow |
bosun trigger |
Trigger reconciliation via daemon |
bosun daemon-status |
Show daemon health and state |
bosun drift |
Detect config drift (--live for fresh check) |
| Command | Description |
|---|---|
bosun yacht up/down/restart/status |
Manage Docker Compose services |
bosun crew list/logs/inspect/restart |
Manage individual containers |
| Command | Description |
|---|---|
bosun provision [stack] |
Render manifest to compose/traefik/gatus |
bosun provisions |
List available provisions |
bosun create <template> <name> |
Scaffold new service |
bosun lint |
Validate manifests |
| Command | Description |
|---|---|
bosun radio test/status |
Test webhook and Tailscale |
bosun mayday |
Show errors, rollback snapshots |
bosun webhook |
Run standalone webhook receiver |
See Commands Reference for full documentation.
Run bosun as a long-running daemon for production GitOps:
# Generate systemd unit files
bosun init --systemd
# Install and start
cd systemd && sudo ./install.sh
# Or run directly
bosun daemonThe daemon provides:
- Unix socket API at
/var/run/bosun.sock - Multi-provider webhooks (GitHub, GitLab, Gitea, Bitbucket)
- Configurable polling with interval-based reconciliation
- Health endpoints (
/health,/ready) for orchestrators - Drift detection with periodic declared-vs-actual state checks
- Circuit breaker stops retrying after 3 consecutive failures
| Variable | Description | Default |
|---|---|---|
BOSUN_REPO_URL |
Git repository URL | Required |
BOSUN_REPO_BRANCH |
Branch to track | main |
BOSUN_POLL_INTERVAL |
Poll interval in seconds | 3600 |
BOSUN_SOCKET_PATH |
Unix socket path | /var/run/bosun.sock |
WEBHOOK_SECRET |
Webhook signature validation | Optional |
Bosun looks for configuration in order:
bosun.yamlin the current directory.bosun.yamlin the current directory$HOME/.config/bosun/config.yaml
# bosun.yaml
root: .
manifest_dir: manifest
compose_file: docker-compose.ymlThese variables are used by
bosun reconcileand the daemon's reconciliation pipeline.REPO_URLandREPO_BRANCHare legacy aliases forBOSUN_REPO_URLandBOSUN_REPO_BRANCH— if both are set, theBOSUN_-prefixed variable takes precedence.
| Variable | Description | Default |
|---|---|---|
REPO_URL |
Git repository URL | Required for reconcile |
REPO_BRANCH |
Git branch to track | main |
SOPS_AGE_KEY_FILE |
Path to age key file | ~/.config/sops/age/keys.txt |
DEPLOY_TARGET |
Remote host for deployment | Local if unset |
┌──────┐ ┌──────────┐ ┌────────────┐ ┌─────────────────┐ ┌──────────────────┐ ┌────────┐ ┌────────┐ ┌─────────────────┐ ┌───────────────┐ ┌────────────┐ ┌────────┐
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ Lock ├────►│ Git Sync ├────►│ Load State │ ├───same─commit───►│ Skip │ │ Template Configs ├────►│ Backup ├────►│ Deploy │ ├────────►│ Compose Up │ ├───────────────────►│ Drift Verify ├────►│ Save State ├────►│ Unlock │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
└──────┘ └──────────┘ └──────┬─────┘ └─────────────────┘ └──────────────────┘ └────────┘ └────┬───┘ └─────────────────┘ └───────────────┘ └────────────┘ └────────┘
│ ▲ │
│ │ │
│ │ │
│ │ │
│ │ │
│ ┌─────────────────┐ │ │ ┌─────────────────┐ ┌───────────────┐
│ │ │ │ │ │ │ │ │
└─────────new─commit──────────►│ Decrypt Secrets ├───────────────┘ └─────failure────►│ Circuit Breaker │ ├───<─3─failures────►│ Alert + Retry │
│ │ │ │ │ │
└─────────────────┘ └────────┬────────┘ └───────────────┘
│
│
│
│
│
│ ┌───────────────┐
│ │ │
└────────────3+─failures───────────►│ Alert + Stop │
│ │
└───────────────┘
Everything uses nautical terminology:
| Term | Meaning |
|---|---|
| Bosun | The CLI tool (receives orders, deploys crew) |
| Captain | GitHub (gives the orders) |
| Yacht | Your server running Docker Compose |
| Crew | Containers |
| Manifest | Service definitions (crew manifest) |
| Provisions | Reusable config templates (supplies stocked aboard) |
| Radio | Webhook/tunnel connection (Tailscale Funnel) |
| Doc | Description |
|---|---|
| Commands Reference | Full CLI documentation |
| Concepts | Architecture and components |
| GitOps Workflow | Reconciliation, polling, triggers |
| Manifest System | DRY service definitions |
| Daemon Architecture | Unix socket API, webhooks, security |
| Alerting | Discord, SendGrid, Twilio notifications |
| CI Pipeline | Dagger-based CI/CD |
| Security | Security considerations |
| Troubleshooting | Common issues and solutions |
| Migration Guide | From bash/Python version |
| GitOps Comparison | Bosun vs Argo CD vs Flux CD |
| ADR | Summary |
|---|---|
| Daemon Architecture | Unix socket API, multi-provider webhooks |
| Council Review | Security-first daemon design (9/10) |
| 0001: Manifest System | DRY crew provisioning |
| 0008: Container vs Daemon | When to use systemd |
| 0010: Go Rewrite | Single-binary CLI |
| 0011: Helm Alignment | Chart-based manifest format |
- Go 1.24+ (building from source)
- Docker + Docker Compose v2
- Git (for reconcile workflow)
- SOPS + Age (for secret encryption)
- Linux or macOS (tested: Unraid, Debian, Ubuntu, macOS)
make build # Build binary
make test # Run tests
make test-cover # Run with coverage
make dev # Development build (no optimizations)
make build-all # Build for all platforms
make ci # Full Dagger CI pipeline
make lint # Run linterSee docs/ci.md for the full CI pipeline.
MIT
