odalr

Run your own cloud on machines you own.

One line on any Linux box joins it to your fleet over an encrypted overlay that punches through NAT. From then on you ask for capacity — a container, a VM, a function, a volume, a hostname — and the platform decides which machine it lands on.

$ curl -fsSL https://acme.odalr.com/install.sh | sudo sh -s -- odalr_join_…

x86_64 and aarch64 · any distribution with apt, dnf, apk or pacman · systemd, OpenRC or runit

Five machines at three sites, each behind NAT, joined over an encrypted overlay to each other and to the control plane. office · NAT home lab · NAT colo · NAT control plane odalr.com · managed rack-a-1 10.224.1.1 rack-a-2 10.224.2.1 home-lab 10.224.3.1 colo-1 10.224.4.1 colo-2 10.224.5.1
What it is

A control plane we run, for servers you keep.

odalr is a managed control plane for machines you own — the rack in the office, the box under a desk, the colo you already pay for. We run the scheduler, the API, the console, the overlay's coordination, DNS and certificates at odalr.com; your workloads, volumes, databases, buckets and Kubernetes nodes run on your hardware and nowhere else. Every server is behind whatever NAT it happens to be behind. Nothing needs a public IP, a port forward, or a VPN you configure by hand. Where a standard exists, the endpoint speaks it — S3, OCI, the OpenAI API, the Postgres wire protocol, ACME, OIDC, OpenTelemetry, ssh — so the tools you already use work on the day you join.

How it works

One line, then the machine is capacity.

Run the installer

odalr token create prints the exact command. The script detects the architecture, downloads a signed agent, checks that it is an ELF executable and not a captive portal's error page, enrols the machine and hands the agent to systemd, OpenRC or runit so something restarts it.

$ curl -fsSL …/install.sh | sudo sh -s -- <token>

The machine joins

The agent dials out; nothing dials in. It gets a stable address on the WireGuard overlay, finds direct paths to its peers by NAT hole punching, and reports CPU, memory, disk, GPUs and runtimes on every heartbeat. It installs what a scheduled workload needs — Docker, QEMU, nftables — from the distribution's own repositories when the work is placed, and never removes anything.

$ odalr nodes
home-lab  ready  10.224.3.1  8  32.0 GiB  docker

Deploy

Ask for a container, a VM, a function, a database, a bucket or a cluster; the scheduler picks the machine. Publish it and it has a hostname and a certificate. Scale it and the replicas spread across machines. Write the whole application as one file and apply it.

$ odalr run web --image ghcr.io/acme/web:v3
$ odalr expose web:8080
 https://web.acme.run.odalr.com → web:8080
Strengths

What it does, and how.

Each of these names the mechanism, because a promise without one is marketing.

No public IP, no port forward, no VPN

Every server is behind whatever NAT it happens to be behind: the agent dials out, and WireGuard between peers finds a direct path by hole punching.

10.224.0.0/12 overlay · disco UDP · relay only when nothing else can

Your own machines carry the traffic

When two machines cannot reach each other, a third one of yours that can reach both carries the WireGuard ciphertext instead of us — it learns nothing a relay would not.

transit chosen from the fleet's reachability matrix · odalr.transit=off opts out

Containers, VMs and functions on one address space

Docker containers, QEMU/KVM guests from cloud images with cloud-init, Firecracker microVMs, and functions with Lambda-shaped or Web fetch handlers — each gets a stable overlay address.

odalr run · odalr vm · odalr fn deploy · Lambda Runtime API on every node

A hostname and a certificate for everything

Publish a service and it gets a name under your account with a Let's Encrypt certificate, or bring a domain and prove it with one TXT record.

<service>.<account>.run.odalr.com · odalr domain add · ACME HTTP-01 and DNS-01

Volumes that survive a machine

A volume kept on two machines is mirrored with DRBD and promoted when the replica itself reports the primary gone; a single copy is rebuilt from its last restic snapshot, and the failover says when that was.

--replicas 2 · --sync for protocol C · odalr volume restore

Kubernetes, on your servers

Upstream k3s on VMs the platform places — one API server or three, never two — where a PersistentVolumeClaim becomes a replicated volume and a LoadBalancer becomes a route with a certificate.

odalr cluster create prod --ha · kubectl as your platform identity · CSI driver

Secrets that never travel in the clear

Sealed at rest, referenced as secret://name, and substituted only as the spec reaches the machine that runs it — so the value never appears in the API, the event stream or a backup.

_FILE convention delivers a tmpfs file · private registry logins get the same treatment

Networks that actually restrict

A private network's members reach each other and nothing else does, compiled to per-node nftables rules; a network that says the internet is blocked drops forwarded traffic outside the overlay.

odalr net create backend --member api --member db · policy swapped atomically

Reach anything, manage over ssh

odalr ssh reaches a VM with no public address and no open port, and the whole management surface is available as ssh odalr.com 'node ls' — with the key you already have.

odalr proxy for scp and rsync · odalr forward for a local port · commands generated from the API

Backups on a different machine

Scheduled copies of a bucket land on another machine with retention, and volume snapshots are taken by restic into the object store — content-chunked, encrypted, and pruned so old ones free their space.

odalr backup · fourteen kept by default · the metrics store is backed up unasked

Storage, registry and metrics live on your fleet

Buckets are a Garage cluster the fleet runs itself, the registry is zot with its blobs in that store, and metrics are VictoriaMetrics — all placed as workloads on your machines like anything else.

no special path for platform components · deleting one is refused by name

Signed, reversible updates

Publish a signed agent binary and every connected machine verifies, swaps and restarts into it in seconds; a build that cannot reach the control plane rolls itself back.

release key pinned at enrolment · --canary 10 exposes a tenth first
A session

From sign-in to a public URL.

Sign in, create a fleet, mint a join token, run its line on a machine, run an image, publish it. The output is the CLI's own. Pipe any command and it is JSON — odalr nodes | jq '.[].name' works without a flag.

Everything here is also a button in the console, and a command over ssh odalr.com.

$ odalr login https://odalr.com
signed in as ann@acme.example at https://odalr.com

$ odalr fleet create acme
fleet acme created
  control plane https://acme.odalr.com

$ odalr token create --uses 10 --ttl 1h
odalr_join_eyJjb250cm9sX3BsYW5l…

  run this on the machine you want to add:

  curl -fsSL https://acme.odalr.com/install.sh | sudo sh -s -- odalr_join_eyJjb…

$ odalr nodes
NAME      ID               STATUS  OVERLAY IP  CPU  MEMORY     RUNTIMES           SEEN
rack-a-1  n-jfq42xpseqey6  ready   10.224.1.1  32   128.0 GiB  docker,qemu        2s ago
rack-a-2  n-3ikfqm5m7oba2  ready   10.224.2.1  32   128.0 GiB  docker,qemu,fire…  1s ago
home-lab  n-xk29dm4pqr8s1  ready   10.224.3.1  8    32.0 GiB   docker             3s ago

$ odalr run web --image ghcr.io/acme/web:v3 --memory 2G
web started
  id       wl-k2m9xp4qrs
  address  10.224.2.2

$ odalr expose web:8080
https://web.acme.run.odalr.com → web:8080
  served from rack-a-2
Standards

Protocols, not dialects.

Where a standard exists and the ecosystem is built on it, the endpoint speaks the standard rather than something of our own design. What is not implemented is refused by name rather than ignored.

SurfaceSpeaksSo this works unchanged
Object storage S3 AWS CLI, boto3, s3fs, rclone, Terraform backends
Container registry OCI Distribution v2 docker login / push / pull, and every CI that speaks them
Model serving OpenAI API Any OpenAI client, LangChain, LlamaIndex, Continue, Aider
Databases Postgres · MySQL · Redis · Mongo wire Every ORM and driver that exists
Document tables DynamoDB boto3 and the DocumentClient
Shared volumes NFS mount
VM configuration cloud-init Every cloud image ships with it
Overlay WireGuard The kernel implementation, audited, everywhere
Certificates ACME (RFC 8555) Let's Encrypt, or any ACME CA
Shell access OpenSSH ssh, scp, rsync, ProxyCommand
Sign-in OIDC + PKCE Google, Okta, Entra, Keycloak, Authentik
Traces and metrics OTLP · OpenMetrics · PromQL · W3C Trace Context Every OpenTelemetry SDK; Grafana with the stock Prometheus source
Queues and topics SQS · SNS Celery, Symfony Messenger, Laravel Queue, aws-sdk-*
Functions Lambda Invoke and Runtime API · WinterCG fetch · ASGI Hono, Elysia, FastAPI, any Lambda container image
Stacks Compose Specification A working docker-compose.yml applies directly
Roles IAM policy documents A policy written for AWS is legible here
The API itself OpenAPI 3.1 /openapi.json, generated from the router it describes
Your machines, our control plane

What runs where, honestly.

A fleet is one database file and a slice of memory that nothing outside it references. Yours is <fleet>.odalr.com, with its own secrets key and its own background loops.

We run

The control plane: the API and console, the scheduler, the overlay's coordination and NAT discovery, the authoritative DNS for published names, certificate issuance and renewal, the agent release pipeline and its signing key, and the upgrades of all of it. It holds your fleet's state — desired workloads, sealed secrets, keys, memberships — not your data.

You run

Every workload. Containers, VMs, functions, databases, Kubernetes nodes, volumes and their replicas, the Garage cluster behind every bucket, the zot registry, the VictoriaMetrics store, the log store. Workloads never leave your servers. Delete the fleet and what was on your machines is still on your machines.

What passes through us

Overlay traffic between two of your machines that cannot reach each other and have no third machine to carry it is relayed by the control plane as WireGuard ciphertext it cannot read. Public HTTPS to a published hostname is terminated at the control-plane tier today and forwarded over the overlay; raw TCP and TLS-passthrough routes are forwarded unread. Certificates on the node edge are the next step, not a promise.

Self-hosting

The control plane is one binary that embeds the console, and the repository's README covers running it yourself with --public-url and three ports. Managed means we carry the upgrades, the run.odalr.com zone, the certificates and the release key — and you keep the option of leaving, because nothing you deployed is anywhere but on your hardware.

Upgrades

Restarts interrupt nothing.

A person using the platform cannot tell a control plane or an agent was upgraded: no refused connection, no dropped session, no reset tunnel, no gap in DNS, no stopped workload.

  1. The control plane hands over; it does not drain. The running process starts the new binary as a child that inherits every listening socket — API, ingress, TLS, DNS, discovery. The child migrates, comes up on the same sockets and reports ready.
  2. Only then does the parent stop accepting and tell every agent to hand over. Each agent opens a second session, which can only land on the child, and keeps the old one open solely for the streams on it — a shell, a tunnel, a log follow — until the last one ends.
  3. In-flight requests finish where they started. Event streams carry a 250 ms retry hint, so a browser reconnects to the child before it notices. If the child never says ready, the parent carries on and the deploy reports a failure with the old process still serving.
  4. Agents do the same on your machines. The new agent inherits the TUN device, the overlay socket, the edge listeners and the resolver, so the datapath never closes; network policy is swapped atomically rather than flushed and rebuilt.

Not seamless, by design: a crash, a machine reboot, and a downgrade across a migration that was not additive. Those are outages and are reported as such.

Pricing

Free while in beta.

Open sign-up. Nothing is capped by default. The control plane is ours to run; the machines are yours, so there is nothing to meter but our own work — pricing for that will be announced before anything is charged.