GitOps for Docker Compose. No Kubernetes required.
Push to git. Bosun receives orders. Containers deploy. Smooth sailing.
Docs site: https://cameronsjo.github.io/bosun/ β guides, ADRs, and the editorial diagram set.
ββββββββββββ βββββββββββββββββββββββββ βββββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββββ ββββββββββββββββββββββ
β β β β β β β β β β β β β β
β 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 installingbosun update requires checksums.txt from the same GitHub release and verifies
the selected compressed archive before extraction or executable replacement. A
missing checksum asset, invalid selected entry, download failure, or digest
mismatch aborts the update and leaves the installed executable unchanged.
bosun update --check checks release metadata only and downloads neither asset.
The same-release SHA-256 manifest detects corruption and mismatched assets, but
it is an integrity check rather than independent publisher authentication. An
attacker who can replace both the archive and checksums.txt is outside this
control. Releases do not currently publish checksums.txt.pem or
checksums.txt.sig; Bosun does not claim checksum signature verification.
# 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 - GitHub webhooks (
/webhook/github) and a generic HMAC endpoint (/webhook). GitLab, Gitea, and Bitbucket run through the separatebosun webhookreceiver, which normalizes each provider and forwards to the daemon - 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_GIT_USERNAME |
Private HTTPS Git Basic-auth username; requires BOSUN_GIT_TOKEN |
Unset |
BOSUN_GIT_TOKEN |
Private HTTPS Git Basic-auth password/token; requires BOSUN_GIT_USERNAME |
Unset |
BOSUN_GIT_FETCH_DEPTH |
Shallow clone/fetch history depth; increase when deploy diffs span multiple commits | 1 |
BOSUN_POLL_INTERVAL |
Poll interval in seconds | 3600 |
BOSUN_SOCKET_PATH |
Unix socket path | /var/run/bosun.sock |
BOSUN_SOCKET_ALLOWED_UIDS |
Additional numeric UIDs allowed to trigger through the Unix socket (comma-separated) | Daemon UID only |
BOSUN_ALLOW_UNAUTHENTICATED_SOCKET |
Disable Unix socket peer-credential authorization (true only; logs security warnings) |
false |
WEBHOOK_SECRET |
Webhook signature validation | Required for webhook triggers (fail-closed) |
BOSUN_ALLOW_UNAUTHENTICATED_WEBHOOK |
Accept unauthenticated webhook triggers (true only; logs a security warning per request) |
false |
BOSUN_LISTEN_ADDR |
Host/IP the HTTP server binds to | All interfaces |
Webhook auth fails closed. With no
WEBHOOK_SECRETset, the daemon's HTTP trigger endpoints (/webhook,/webhook/github,/webhook/manual) reject every request with403. To restore the old accept-anything behavior on a trusted network, setBOSUN_ALLOW_UNAUTHENTICATED_WEBHOOK=trueexplicitly. The Unix socket trigger (bosun trigger) is unaffected.
Unix socket mutation auth also fails closed. On Linux, the daemon UID and numeric UIDs in
BOSUN_SOCKET_ALLOWED_UIDScan trigger reconciliation. A missing peer credential (including on non-Linux platforms) returns403. SetBOSUN_ALLOW_UNAUTHENTICATED_SOCKET=trueonly when socket permissions are intentionally the entire trust boundary.
For a private HTTPS repository, set BOSUN_GIT_USERNAME and
BOSUN_GIT_TOKEN together. Bosun sends them only to the configured HTTPS
origin and rejects plaintext, cross-origin, downgrade, partial-pair, and
URL-userinfo configurations before network access. Leave both unset for
anonymous HTTPS. These new variables have no unprefixed aliases and apply to
the effective URL after BOSUN_REPO_URL takes precedence over REPO_URL.
Remove credentials from the URL itself, and restart Bosun after rotation;
project configuration reload does not read or rotate the pair.
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 |
BOSUN_GIT_USERNAME |
Private HTTPS Git username; pair with BOSUN_GIT_TOKEN |
Unset |
BOSUN_GIT_TOKEN |
Private HTTPS Git token; pair with BOSUN_GIT_USERNAME |
Unset |
SOPS_AGE_KEY_FILE |
Path to age key file | ~/.config/sops/age/keys.txt |
DEPLOY_TARGET |
Remote host for deployment | Local if unset |
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 1. Acquire Lock β
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 2. Git Repository Sync β
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 3. Reload Config + Check State / Commit / Path Skips + Breaker β
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β Deploy required? βββββββ¬βββββββββββββββββββββββ
β β β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ ββββββββββββββββbreakerβΌtrippedββββββββββββββββββββββββββββββββββββββββββββββββ
β β β
no change / noβrelevant paths β yes
β β β
β β β
βΌ βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β β β β
β Clean Skip / Reset Breaker + Release Lock β β Circuit Breaker Halt / Alert + Error Return β β 4. Track Attempt + Decrypt / Resolve Deploy Mode β
β β β β β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββ
β
β
β
β
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β 5. Render Private Staging Tree βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 6. Extract Declared Services / Fail Closed if Missing β
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 7. Create + Verify Rollback Backup β
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 8. Snapshot Health / Mark NeedsRedeploy β
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 9. Deploy Managed Files / Verify Writes / Transfer β
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 10. Docker Compose Up / Signal Reload β
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 11. Optional Health Gate ββββββββββββββββββββββββββββββ
β β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ healthy /βdisabled
β β
failed β
β β
β β
βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββββββββ
β β β β
β Rollback Full Tree / Fail Deploy, Retry Next Cycle β β 12. Execute Post-Sync Hooks β
β β β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββ¬βββββββββββββββββββββββ
β
β
β
β
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β 13. Local Post-Deploy / Health + Drift Check ββββββββββββββββββββββββββββββ
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 14. Finalize Staging Evidence β
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 15. Retain Verified Backups / Record Success β
β β
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
β
β
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 16. Release Lock β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β Trigger β
β β
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β
β
β
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β Reconciliation running? ββββββββββββββββββββββββββββββββββββββββ
β β β
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ no
β β
yes β
β β
β β
βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β β
β Add to pendingTrigger batch / preserve source and force β β Acquire /var/run/bosun/reconcile.lock / flock LOCK_EX | LOCK_NB β
β β β β
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β β
β β
β β
β β
βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β β
β Return 202 Accepted β β Run Reconciliation β
β β β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β
β
βββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β β
βΌ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β pendingTrigger batch queued? ββββΌββββββββββββββββββββββββββββββββββββ
β β β β
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ β no
β β β
yes β β
β β β
β β β
βΌ β βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β β β
β Run One Coalesced Reconciliation β β β Finish β
β β β β β
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
βββββββββββββββββββββββββββββββββ
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, webhook reception, standalone receiver split |
| 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.25+ (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
