Releases: kemeter/ring
Release list
v0.10.0
This release significantly broadens runtime support and hardens scheduler reliability.
Highlights
- New runtimes: containerd (native gRPC, CNI, multi-arch), Podman, and Firecracker microVM (experimental — boot, networking/NAT, virtio-block volumes, jobs, metrics and console logs).
- Private registries: pull private images via
image_pull_secretor the host's Docker credentials (use_host_auth), and scoped API tokens (PAT) with per-scope/per-namespace enforcement. - Outbound webhooks with HMAC-signed delivery and a durable event queue.
- Observability: Prometheus
/metricsendpoint (inventory, queues, per-deployment usage). - First-class volumes (
/volumesCRUD +ring volumeCLI).
Reliability
- No more infinite recreation loops: a worker that exits 0 is marked
Completed,MAX_RESTART_COUNTis honoured,restart_countis reset for a worker stuck inCreating, and astart_periodgrace window applies before the readiness deadline.
Full changelog: see CHANGELOG.md.
Ring v0.9.0
Changed (breaking)
-
POST /deploymentsnow uses RFC 7807 with the same shape asPOST /users(application/problem+json,violations[]withproperty_path,message,code). Existing 400/422 responses with{"message": "..."}body are replaced. Codes for the rules already in place:deployment.runtime.unsupported— runtime must be one of: docker, cloud-hypervisordeployment.command.cloud_hypervisor_unsupporteddeployment.image.cloud_hypervisor_requires_absolute_pathdeployment.network.host_runtime_unsupporteddeployment.ports.host_network_conflictdeployment.replicas.host_network_conflict
New rules (previously not validated, the manifest applied and broke at runtime):
deployment.ports.published.out_of_range/deployment.ports.target.out_of_range— port 0 is reserveddeployment.ports.published.duplicate— two entries publishing the same host portdeployment.ports.replicas_conflict+deployment.replicas.ports_conflict— publishing host ports withreplicas > 1causes inter-replica collisionsdeployment.replicas.job_must_be_one—kind: jobis one-shotdeployment.health_checks.job_readiness_unsupported— readiness checks only gate rolling updatesdeployment.environment.key.invalid— env var names must match[A-Za-z_][A-Za-z0-9_]*deployment.resources.{limits,requests}.{cpu,memory}.invalid— invalid quantity stringdeployment.config.image_pull_policy.unsupported— must beAlways,IfNotPresentorNever
property_pathfollows JSONPath conventions for nested collections:ports[0].published,volumes[2].source,resources.limits.cpu. -
POST /namespacesnow uses RFC 7807. Validation failures returnapplication/problem+json(422) with codes:namespace.name.length— must be 2 to 63 charactersnamespace.name.format— lowercase DNS-label rules (a-z0-9plus-, no leading/trailing dash)
Conflicts (duplicate name) now return
application/problem+json(409) withtitle: "Conflict"and adetailline naming the offending namespace, instead of the legacy{"error": "..."}shape. -
POST /secretsnow uses RFC 7807. Validation codes:secret.namespace.length/secret.namespace.formatsecret.name.length— 2 to 253 characterssecret.name.format— DNS-subdomain rules (lowercase alphanumerics plus.and-)secret.value.length— 1 to 1 MiB (matches Kubernetes' Secret limit)
404 (namespace missing) and 409 (duplicate) responses are problem+json with
Not Found/Conflicttitles. -
POST /configsandPUT /configs/{id}now use RFC 7807. Validation codes:config.namespace.length/config.namespace.formatconfig.name.length— 1 to 253 charactersconfig.name.format— same DNS-subdomain rules as secretsconfig.data.length— 1 to 1 MiBconfig.data.invalid_json— on PUT, whendatais non-empty but doesn't round-trip as JSONconfig.labels.length— at most 1000 characters
The previous 400 with
{"error": "Validation failed", "details": ...}is replaced by a 422 with violations. 404 (config missing on PUT) and 409 (duplicate on POST) are problem+json. -
POST /loginnow emits problem+json on 401/500 withtitle: "Unauthorized"anddetail: "invalid credentials"(same shape on internal errors with a generic detail). The legacy{"errors": ["Invalid credentials"]}body is gone. -
Validation errors on
POST /usersandPUT /users/{id}now use RFC 7807. The 422 response shape changed from the barevalidator-derived{"errors": <map>}toapplication/problem+json:{ "type": "about:blank", "title": "Validation failed", "status": 422, "detail": "username: must be 2 to 50 characters\npassword: must be 8 to 128 characters", "violations": [ { "property_path": "username", "message": "must be 2 to 50 characters", "code": "user.username.length" }, { "property_path": "password", "message": "must be 8 to 128 characters", "code": "user.password.length" } ] }Every violation carries a stable
codeslug (e.g.user.username.format) that clients can branch on without parsing the human message. All applicable rules run on every request — the response lists every failure in one shot instead of stopping at the first.Username format is now
[a-zA-Z0-9][a-zA-Z0-9._-]*(2-50 chars), matching GitHub-style conventions for human-facing identifiers. Password rules unchanged (8-128 chars). -
DeploymentStatusis now snake_case in the JSON API and DB. Previously the lifecycle states (pending,running, …) were lowercase while the error states (CrashLoopBackOff,ImagePullBackOff, …) were PascalCase — the mismatch silently dropped rows from string-matching filters elsewhere in the code (root cause of PR #84). All variants now share the same convention. Mapping for external consumers:CrashLoopBackOff→crash_loop_back_offImagePullBackOff→image_pull_back_offCreateContainerError→create_container_errorNetworkError→network_errorConfigError→config_errorFileSystemError→file_system_errorError→error(unchanged shape, lowercased)
Migration
20220101000015_snake_case_deployment_status.sqlrewrites existing rows. Update any script that doesjq '.status == "CrashLoopBackOff"'or similar.Event
reasonstrings (ImagePullBackOff,InstanceCreationFailed, …) stay PascalCase — those are event labels, not statuses.
Added
-
Host-memory admission control. Before creating a Docker container or booting a Cloud Hypervisor VM, Ring now checks the deployment's requested memory (
resources.requests.memory, falling back toresources.limits.memory) against the host's currently-available memory. If it doesn't fit, the deployment goes to a new terminal statusinsufficient_resourceswith an event naming the gap (needs X MiB but only Y MiB is available — free memory or lower requests/limits), instead of starting and being OOM-killed (Docker) or failing the VM spawn opaquely (CH). The status is terminal — Ring does not crash-loop, since the memory won't reappear on its own. The check is best-effort and point-in-time, and applies to memory only (CPU overcommit is left alone). Deployments that declare no memory request or limit are not gated. -
volumes: type: secret— mount aring secretas a read-only file inside the container. The decrypted value becomes the file contents, with nokey:field (a secret holds a single opaque value). Pattern matchestype: configbut reads from the encrypted secret store instead of the plaintext config store. Use when an app expects a credentials file path rather than an env var (Prometheuscredentials_file, TLS material, etc.). The mount is always read-only; rotation requires a redeploy. See Deploy with secrets → Mount a secret as a file. -
ring apply,ring namespace createandring secret createrender RFC 7807 problem details. On validation failure the CLI prints the title line plus every violation with its property path, e.g.Unable to apply deployment 'nginx': Validation failed (422) * ports[0].published: must be between 1 and 65535 * replicas: replicas > 1 (3) is incompatible with `ports` — drop `ports` or reduce `replicas` to 1instead of the legacy
API returned status 422: <raw body>one-liner. Non-validation problems (404, 409) print the same way with the server'stitleanddetail. Non-7807 responses fall back to the previous behaviour.
Changed
- Failed Docker image pulls now surface an actionable reason instead of a raw daemon dump. A
ImagePullBackOffevent previously readFailed to pull image '…': <bollard string>. Ring now classifies the failure and rewrites it: authentication refused →registry authentication failed … — check config.server, config.username and config.password; registry unreachable (connection refused, host not found, timeout) →cannot reach the registry … — is it up and the registry host correct?; missing tag/digest stays asnot found. The original daemon string is preserved verbatim in(original error: …). The deployment status (image_pull_back_off) and event reason are unchanged.
Dashboard (new)
- Web dashboard (SvelteKit, served embedded by
ring server start --dashboardor locally viaring dashboard). Login, Overview home page with summary cards (deployments by status, namespaces/secrets/configs counts, node health, failing deployments), and a deployments list with namespace filters and a created-at column. - Deployment detail page — overview, configured resources, running instances, live metrics (per-instance and aggregated CPU / memory / network I/O / disk I/O / PIDs), ports, volumes, environment, configured health checks, health-check probe history, streamed logs (live tail over SSE), and a recent-events timeline.
- Node page — host info (hostname, OS, arch, uptime, CPU cores, memory, load average).
- Read-only views for namespaces (with per-namespace audit trail), secrets, and configs.
- Dark/light theme toggle (persisted, follows
prefers-color-scheme) and the Ring version shown in the sidebar. - Per-page browser titles, copy-to-clipboard on IDs, and a responsive layout for small screens.
Added
ring init— interactive setup that prompts for runtime + port and generatesRING_SECRET_KEY, plus--runtime/--portflags to script it non-interactively (CI, Ansible) without a TTY.ring node get— host information for the server's machine.- Startup banner —
ring server startprints a Vite-style banner with the API's Local/Network URLs, the dashboard URL (when enabled), and the registered runtimes. - Semantic CLI colours and aligned tables — errors red, success green, s...
v0.8.0
[0.8.0] - 2026-05-12
Added
- Cloud Hypervisor — readiness gate: scheduler-side
is_ready_to_drainwith per-health-check anti-flap window. Rolling updates wait for the new instance to be ready before draining the parent. Includes DockerHEALTHCHECKtranslation so the same gate applies to both runtimes (PR #72). - Cloud Hypervisor —
kind: job: dispatch worker/job inapply, boot one VM, markCompletedon guest shutdown. E2Et21_job_kind.shvalidates the full lifecycle including artifact cleanup. - Cloud Hypervisor — command health checks: in-guest
ring-agentover AF_VSOCK port 2375 reads the real exit code (PR #69). - Cloud Hypervisor — full stats parity: CPU and memory from
/proc/<vmm-pid>/{stat,status}(PR #70), then network from/sys/class/net/<tap>/statistics/*(swapped host↔guest), threads from/proc/<vmm-pid>/status, disk I/O from/proc/<vmm-pid>/iowhen accessible (PR #78). Disk I/O degrades gracefully to zero on hardened hosts because CH clearsPR_SET_DUMPABLE. - Cloud Hypervisor — console log rotation: size-based rotation with a 60s sweep, configurable via
[runtime.cloud_hypervisor].max_console_log_bytes/max_console_log_backups(defaults 10 MiB × 3 backups).ring deployment logs --tail Nreads through rotated backups (PR #77). - Cloud Hypervisor — port conflict detection: pre-check
TcpListener::bindbefore VM boot, emitPortAllocationFailedevent andCrashLoopBackOffafterMAX_RESTART_COUNT— same semantics as Docker Compose. - Cloud Hypervisor —
ring doctorsocat check: verifysocatpresence when port mapping is requested. - Docker — host network mode:
network_mode: hostfield on Docker deployments, with migration20220101000014_network_mode.sqlanddocumentation/how-to/use-host-network.md. - Scheduler — configurable anti-flap window:
min_healthy_timeper health check variant (TCP/HTTP/Command), default 10s, scheduler picks the max across readiness HCs. - API — config filtering:
GET /configs?name=.... - API —
ForceReplaceevent: emitted when a rolling update is skipped (PR #71). - Log level classification: extended
classify_logfor kernel (<N>syslog priority,BUG:/Oops:/Kernel panic), cloud-init/systemd (ERROR/WARN/INFO:/DEBUG), and bracketed firmware markers ([INFO]/[WARN]/[ERROR]/[DEBUG]). Runtime-agnostic — benefits both Docker and CH. - Documentation restructure to Diátaxis: tutorials, how-to, reference, concepts, help. Sozune integration added as recommended HTTP proxy.
- Pre-built release binaries: GitHub Actions workflow now attaches
ring(x86_64-unknown-linux-gnu) andring-agent(x86_64-unknown-linux-musl, static) tarballs to each tagged release.
Changed
- Health checks (Docker + CH) migrated to a shared
probemodule. - E2E tests split into
tests/e2e/docker/andtests/e2e/cloud-hypervisor/with arun.shorchestrator. - Cloud Hypervisor stats and logs documented as Supported in the parity table (no longer "partial").
Fixed
- Cloud Hypervisor cleans up half-created VMs on boot failure.
- Cloud Hypervisor retries on transient boot failures with exponential backoff and typed errors.
- Scheduler emits docker-events at level
warning(wasinfo). - Anti-flap window no longer re-arms every scheduler cycle (PR #72).
- Docker
commandhealth check now honors the exit code (PR #72). handle_rolling_updateno longer spawns/kills in a loop when the parent finishes draining (PR #72).- CLI
applyserialises thereadinessflag through to the API (PR #72). RING_SECRET_KEYis validated at startup and surfaced inring doctor.- Config loader falls back to
current = truecontext when the requested name does not match. - OpenSSL CVEs (Dependabot high + moderate) addressed via
cargo upgrade.
v0.7.0
Added
- Cloud Hypervisor runtime: experimental lightweight VM runtime alongside Docker, with per-instance sparse disk copies, TAP networking managed via
CAP_NET_ADMIN, socket-based instance discovery, logs, health checks, and stats. runtime.cloud_hypervisorconfiguration section withfirmware_path.ring doctorcommand to check runtime dependencies.ring deployment health-checksCLI command.--output jsonflag ondeployment listanddeployment inspect.- Multi-runtime scheduler dispatch.
- E2E test scenarios: shell-based create/delete, bind volume, TCP health check, rolling update, replicas convergence, Cloud Hypervisor boot/delete.
Changed
- Unified
RuntimeInterfaceinto a singleRuntimeLifecycletrait. - Removed Docker from
AppState; API now usesRuntimeMapfor instance listing. - API defaults to binding
0.0.0.0. - Cloud Hypervisor uses pre-built raw disk images instead of Docker-to-rootfs conversion.
- Cloud Hypervisor data directories moved to
~/.config/kemeter/ring.
Fixed
- Dropping a deployment now removes its containers regardless of status (including
exited,dead, etc.). - API bind errors are handled gracefully instead of panicking.
- Health check removal is runtime-aware.
- Per-instance disk copies, qcow2 fallback, VM state refresh.
- Instance discovery by scanning sockets instead of relying on in-memory state.
- Volumes and command health checks are rejected on the Cloud Hypervisor runtime (not yet supported).
v0.6.0
Added
- Configurable CORS origins allowlist.
- Marketing site and documentation portal (
website/) built with aplos. RING_TOKENenv var to bypassauth.json.- Granular CLI exit codes (auth, connection, not-found, conflict).
--follow,--tail,--since,--containerflags ondeployment logs.command,resources,health_checksfields supported inapply.namespace pruneonly removes inactive deployments by default, with--allflag.
Changed
- Scheduler abstracted behind
RuntimeLifecycletrait. - Config volumes resolved in scheduler and passed as
ResolvedMountto the runtime. - Single injected Docker instance instead of reconnecting in every function.
- Async I/O for temp files;
&strin model queries instead ofString.
Fixed
- Scheduler no longer overwrites
deletedstatus set by the API. user updaterequires at least one field.
v0.5.0
Added
- Rolling update strategy with
parent_idcoordination. - CI workflow with clippy and formatting checks.
- Input validation on username and password.
- Prevent users from deleting their own account.
Changed
- Scheduler loop split into smaller focused functions.
- Duplicated SQL filter logic extracted into
models::queryhelper. - Errors propagated via
thiserrorinstead of being silently swallowed. SCHEDULER_INTERVALenv var renamed toRING_SCHEDULER_INTERVAL.
Fixed
- Named volumes cleaned up on deployment delete, with driver config passed through.
- Unknown volume types rejected instead of silently falling back to config.
- Config volume temp files cleaned up; duplicates avoided.
- Container removal failures during rolling update handled.
- Containers of
CrashLoopBackOffdeployments deleted when removed.
v0.4.0
What's new
Secrets management
- AES-256-GCM encrypted secrets
secretRefsupport in deployment environment variables- Warn when deleting a secret referenced by deployments
Namespaces
- Namespaces as first-class resource with auto-creation
- Namespace support in YAML config files
Resource limits
- Kubernetes-style resource limits/requests (
512Mi,0.5CPU)
Security
- Whitelist allowed filter columns
- Image digest field on deployments