- Go 98.3%
- Shell 1.5%
- Dockerfile 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo | ||
| .idea | ||
| cmd | ||
| docs | ||
| internal | ||
| packaging | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| main.go | ||
| README.md | ||
shove
Push-triggered Docker Compose deploys from an infra Git repository.
You keep your Compose stacks in one Git repository. On every push, CI runs shove deploy, which
compares each stack at the pushed commit with what every server actually runs, and tells the
servers to deploy, redeploy or tear down stacks until they match. Servers fetch the exact commit
themselves and run docker compose; CI never copies files to them.
push ──▶ CI: shove deploy ──(tailnet)──▶ shove agent on each server ──▶ git fetch + docker compose up
- Declarative: Git is the source of truth. Deleting a stack, or removing a server from its
targets, tears it down there. - Minimal redeploys: only stacks whose directory changed are deployed, and within a stack only the services whose definition or files changed are recreated.
- Safe by default: validation runs before anything is sent, a delayed CI run never downgrades a newer deploy, and a stack is never taken over from another repository.
- Secrets: sops-encrypted secrets in the repository, decrypted in memory on the server, and per-server secrets files. Secret values are redacted from all output.
- GitHub Actions and Forgejo Actions, with
shove init cito generate the workflow.
Quick start
1. Install the agent on each server (needs git and Docker Compose 2.24+):
curl -fsSL https://code.jonasclaes.be/jonasclaes/shove/releases/download/latest/install.sh | sudo sh
curl -fsSLO https://code.jonasclaes.be/jonasclaes/shove/releases/download/latest/shove-agent.service
sudo mv shove-agent.service /etc/systemd/system/
sudo systemctl edit shove-agent # override ExecStart= to add --allowed-repository <repo>
sudo systemctl enable --now shove-agent
The agent has no authentication of its own: keep port 4212 reachable only from CI, for
example over Tailscale. See docs/agent.md.
2. Lay out the infra repository:
shove.yaml # inventory
stacks/
whoami/
compose.yaml
shove.yaml # which servers run this stack
secrets.enc.env # optional, sops-encrypted
# shove.yaml
version: 1
servers:
web-1:
url: http://web-1.example.ts.net:4212
# stacks/whoami/shove.yaml
targets: [web-1]
secretsFile: secrets.enc.env # optional
3. Add the CI workflow from the root of the infra repository:
shove init ci # GitHub, or a detectable Forgejo
shove init ci --provider forgejo # self-hosted Forgejo
Add the Tailscale OAuth secrets (see docs/ci.md) and push.
Commands
| Command | What it does |
|---|---|
shove deploy [stack…] |
Reconcile every server with head, or deploy the named stacks. |
shove plan |
Offline: validate the repository and list the stacks that changed. |
shove status |
Show what every server runs, with commit and health. |
shove rollback <stack> |
Redeploy each target's previous commit. |
shove undeploy <stack> |
Tear a stack down (named volumes are kept). |
shove init ci |
Write the CI workflow. |
shove agent |
Run the agent. |
See docs/operations.md for flags, exit codes and reconcile rules.
Documentation
- Repository layout and
shove.yaml, including secrets - Installing and running the agent
- CI setup for GitHub and Forgejo
- Operations: commands, reconcile rules, rollback, undeploy, troubleshooting
- Implementation notes: design decisions and manual checks
Development
Requires Go (see go.mod), git and Docker with Compose. The sops tests also need sops and
age-keygen, and skip without them.
go build -o shove .
go test ./... # unit tests
go test -tags integration ./... # against the local Docker daemon
go test -tags e2e ./cmd -run TestE2E -v # two agents in docker:dind
CI and releases run on Forgejo (.forgejo/workflows/). Pushing a v* tag builds the binaries
and publishes a release with install.sh and the systemd unit.