CraneOps
A minimal GitOps + Operator framework for Docker Swarm
Overview
CraneOps is an open-source GitOps + Operator framework written in Go
for self-hosters: declarative YAML CRDs to manage Docker Swarm apps and infrastructure.
It provides a simple way to manage Docker Swarm apps and infrastructure declaratively.
CraneOps keeps your cluster in sync with manifests:
- Stored locally under
./data/manifests
- Sourced from a Git repo (poll + webhook)
- Applied manually via the CLI
It continuously detects drift and reconciles Docker resources back to their desired state.
Key Features
- Declarative YAML manifests stored on disk or in Git
- Drift detection & self-healing
- Background reconcile loop (
--resync.interval
)
- One-shot reconcile on startup
- Manual refresh:
crane list --refresh [--force]
- GitOps support: poll repos or trigger from webhooks
- Secrets management: seal secrets with server’s pubkey
- HTTP API + CLI for full automation and inspection
- Lightweight: no Kubernetes, no external dependencies
Supported Kinds
- App — Docker services (with Traefik labels, env, secrets, configs)
- Network — overlay or bridge networks
- Secret — immutable Docker secrets (with sealing support)
- Config — immutable Docker configs
- Volume -- persistent named Docker volumes
Installation
Build from source
git clone https://git.mills.io/prologic/craneops.git
cd craneops
make build
Running the Server
Local manifests
./craned \
-addr :8080 \
-manifests ./data/manifests \
-state ./data/state
GitOps mode
./craned \
-addr :8080 \
-git.url https://gitea.example.com/infra/craneops-manifests.git \
-git.user git \
-git.pass "$GITEA_TOKEN" \
-git.branch main \
-git.path ./git \
-git.subdir envs/prod \
-git.interval 30s \
-webhook.secret 'supersecret'
CLI
crane apply -f <file|dir> [--reconcile]
crane get <kind> <name>
crane list [--kind Kind] [--refresh] [--reconcile]
crane delete <kind> <name> [--reconcile]
crane pubkey
crane seal -f secret.yaml
crane login --user USER --pass PASS
Examples:
crane apply -f app/whoami.yaml
crane list --refresh --reconcile
crane delete App whoami --reconcile
API
GET /healthz
GET /api/v1/kinds
GET /api/v1/resources?kind=App&includeStatus=1
GET /api/v1/resources/{kind}/{name}
POST /api/v1/apply?reconcile=1
PUT /api/v1/resources/{kind}/{name}?reconcile=1
DELETE /api/v1/resources/{kind}/{name}?reconcile=1
POST /webhooks/gitea
(HMAC: X-Gitea-Signature
)
Data Layout
data/
manifests/ # desired state (YAML)
app/whoami.yaml
network/traefik.yaml
secret/db-password.yaml
state/ # runtime state
status/<Kind>/<name>.json
keys/sealer.key
Example: Listing resources
$ crane list --refresh
KIND NAME STATUS SYNC DRIFT PATH
App whoami Ready OK NO app/whoami.yaml
Network traefik Ready OK NO network/traefik.yaml
Secret db-password Ready OK NO secret/db-password.yaml
Roadmap
See Roadmap
License
Licensed under the MIT License.
Logo from Flaticon