Kubernetes for Rust.
Deploy, manage, and monitor Rust services across a cluster of servers from one CLI.
| Capability | What it does |
|---|---|
| Multi host clusters | Deploy to a fleet of servers, not just one |
| Rolling updates | Hosts updated one at a time with health gates |
| Canary & blue-green | Deploy to a subset, or spin a full parallel stack |
| Embedded reverse proxy | Built in HTTP proxy with Host based routing. No nginx needed. |
| Declarative state | Declare targets in sortie.toml; sortie apply reconciles |
| Auto rollback | Failed health checks trigger automatic rollback |
| Service registry | Register services, resolve by name, generate ingress config |
| Secrets management | Encrypted key-value store on servers (SSH is the boundary) |
| Auto scaling | CPU-based scaling loop, adjusts systemd instances |
| Self healing | sortie-agent watchdog restarts dead services |
| kubectl style CLI | apply, get, describe, logs, rollback, scale |
| Multi cluster federation | Deploy to all targets with one command |
| TLS provisioning | Auto certs via certbot (Let's Encrypt) |
| Observability | Per-host metrics (CPU, memory) and log aggregation |
| Environment config | Inline env vars for per-target service configuration |
cargo install sortie
sortie init
Edit sortie.toml with your server details, then deploy:
sortie apply production
This builds your binary, uploads it via SCP to every host, installs a systemd service, starts it, and waits for the health endpoint to respond. If a health check fails, that host rolls back.
To see what would change without deploying:
sortie apply production --check
No nginx, no caddy, no setup. Install the proxy on your server and it reads the service registry to route requests.
sortie svc register production my-api 8080
sortie proxy install production --port 80
The proxy matches the Host header against registered service names. If your server IP is 1.2.3.4:
curl -H "Host: my-api" http://1.2.3.4/
No domain needed. The service name you registered is the hostname clients send. You can use any string.
To add TLS, run sortie tls setup with a real domain first (Let's Encrypt), then point DNS at the server.
Roll out to a subset of hosts before full deploy:
sortie apply production --canary 20
Or deploy a full parallel stack and flip:
sortie apply production --blue-green
sortie deploy all
Iterates every target in sortie.toml. Also works with --canary and --blue-green.
cargo install sortie
Or build from source:
git clone <repo-url>
cd sortie
cargo build --release
Requires Rust 1.70+, Linux servers with SSH + systemd, key-based auth.
| Command | Like kubectl | Action |
|---|---|---|
sortie init |
Create a sortie.toml | |
sortie apply <target> |
kubectl apply | Rolling deploy across all hosts. --check for dry-run, --canary/--blue-green for strategies |
sortie deploy <target> |
Same as apply, with --all for federation |
|
sortie get |
kubectl get | Show all targets with version, timestamp, services |
sortie describe <target> |
kubectl describe | Target config + per-host status |
sortie logs <target> [host] |
kubectl logs | Fetch service logs from a host |
sortie rollback <target> |
kubectl rollout undo | Revert to previous binary |
sortie status <target> |
kubectl get pods | Check service status per host |
sortie health <target> |
Check host connectivity and service health | |
sortie svc register <target> <name> <port> |
Register a service in the cluster | |
sortie svc list <target> |
List all registered services | |
sortie svc resolve <target> <name> |
Resolve a service name to host:port | |
sortie svc restart <host> <target> <name> |
kubectl rollout restart | Restart a service on a host |
sortie svc stop <host> <target> <name> |
Stop a service on a host | |
sortie svc start <host> <target> <name> |
Start a service on a host | |
sortie proxy install <target> |
Install embedded reverse proxy as a systemd service | |
sortie scale <target> <n> |
kubectl scale | Set systemd instance count per host |
sortie autoscale <target> |
Start CPU-based auto-scaling loop | |
sortie secret set <target> <key> <value> |
Store a secret file on the server | |
sortie secret get <target> <key> |
Read a stored secret | |
sortie secret rm <target> <key> |
Delete a stored secret | |
sortie tls setup <target> <domain> <email> |
Provision Let's Encrypt certs via certbot | |
sortie ingress <target> |
Generate nginx config from service registry | |
sortie metrics <target> |
Show CPU and memory per host |
[targets.production]
hosts = ["10.0.0.1", "10.0.0.2"]
port = 22
user = "deploy"
key_path = "~/.ssh/id_rsa"
target_triple = "x86_64-unknown-linux-gnu"
deploy_path = "/opt/myapp"
health_check_url = "http://localhost:8080/health"
health_check_timeout_secs = 30
pre_deploy = "cd /opt/myapp && ./run_migrations.sh"
post_deploy = "echo 'Deployment completed'"
[targets.production.service]
name = "myapp"
restart = "always"
[targets.production.env]
DATABASE_URL = "postgres://localhost:5432/myapp"
RUST_LOG = "info"| Field | Required | Default | Description |
|---|---|---|---|
hosts |
yes | Server addresses in the cluster | |
port |
no | 22 | SSH port |
user |
yes | SSH user | |
key_path |
no | ~/.ssh/id_rsa |
SSH key path |
target_triple |
yes | Rust target triple | |
deploy_path |
yes | Server directory for binary | |
health_check_url |
no | URL to check after deploy | |
health_check_timeout_secs |
no | 30 | Max wait for health |
instances |
no | 1 | Expected replicas per host |
pre_deploy |
no | Command to run on each host before the new binary takes traffic | |
post_deploy |
no | Command to run on each host after the deploy succeeds | |
service.name |
yes | systemd service name | |
service.restart |
no | always |
Restart policy |
env.* |
no | Environment variables |
Reads desired state from sortie.toml. Builds the binary. Performs a rolling deployment across all hosts: upload via SCP, install systemd unit with env vars, verify health, next host. If a health check fails, that host rolls back and the deploy stops.
Previous versions are kept as .bak files for instant rollback. Cluster state is stored in .sortie/state.json on each host.
sortie/
Cargo.toml
Cargo.lock
LICENSE
README.md
CHANGELOG.md
CONTRIBUTING.md
CODE_OF_CONDUCT.md
SECURITY.md
SECURITY_CONTACTS
SUPPORT.md
OWNERS
OWNERS_ALIASES
.gitattributes
.gitignore
rust-toolchain.toml
Makefile
cmd/
sortie/
main.rs
agent/
main.rs
pkg/
lib.rs
cli.rs
types.rs
config.rs
init.rs
build.rs
ssh.rs
cluster.rs
deploy.rs
systemd.rs
health.rs
rollback.rs
proxy.rs
logo/
sortie.png
build/
build-release.bash
hack/
ci.bash
tests/
smoke_test.rs
docs/
architecture/
images/
c4-context--0.svg
c4-dynamic-build--0.svg
build-lifecycle--0.svg
c4-containers--0.svg
c4-components--0.svg
c4-deployment--0.svg
LICENSES/
README.md
examples/
basic.toml
multi-host.toml
with-env.toml
third_party/
README.md
CHANGELOG/
v0.1.0.md
v0.2.0.md
Pull requests welcome. See CONTRIBUTING.md.
MIT. See LICENSE.
