Skip to content

One control planefor any compute.

Deploy apps, databases. Run jobs and sandboxes. Whatever runs in a container, with HTTPS, logs and private networking built in, on any machine you connect or on Unisrv capacity.

Read the docsInvite-only private alpha

Bare metal, a VPS, a cloud VM and a homelab server you own, plus Unisrv capacity, all connect to one Unisrv control plane, which places your applications across them: web on the homelab server, postgres with its volume on the bare-metal server, and a one-off migration that ran and exited on Unisrv capacity.

Connect a machine. Deploy.It's live, from any hardware.

Run the command the console gives you on any Linux machine. The node dials out, so NAT & firewalls are fine. Then treat Unisrv as any streamlined PaaS, but using your hardware for compute.

The runtime and local orchestrator are conveniently packaged as a container image, so all you need is Docker or Podman to run it privileged for spawning containers or microVMs.

  1. Connect
  2. Deploy
  3. Live
root@homelab-nucyour server
docker run -d --name unisrv-node \
--privileged --stop-timeout 120 \
-v unisrv:/srv/unisrv \
ghcr.io/unisrv/node unc_eyJub2RlX2lkIjoiN2YzYzlhMmUi…
docker logs -f unisrv-node
09:14:06 Node Id: 7f3c9a2e-41d8-4b6a-9e0f-2c5d8b1a6e47, tunnelling to relay sto1
09:14:06 Spawned routr (PID: 41), tunnelling to relay-sto1.unisrv.io:443
09:14:06 Enrolling node 7f3c9a2e-41d8-4b6a-9e0f-2c5d8b1a6e47 with service backend
09:14:07 Node service is up and running...
09:14:07 Successfully enrolled and saved certificates
09:14:07 Rerolling backend connection
09:14:11 Accepted provision for 0b41e6d2-93c7-4f15-8a2b-d6e0c4f71a39
09:14:12 Image ready: ghcr.io/acme/web:1.4 -> sha256:9e1f04c2a7b3… 48.2 MB in 1.2s
09:14:13 Instance 0b41e6d2-93c7-4f15-8a2b-d6e0c4f71a39 state event: StateChange(ExecutingContainer)
~/myappyour laptop
unisrv up -y
Plan for project myapp:
· environment production (using existing)
+ service web
hosts: app.example.com
+ deployment web
image: ghcr.io/acme/web:1.4
replicas: 1
Summary: 2 to add, 0 to change, 0 to recreate, 0 to destroy.
+ 🔀 service web created
+ 🚀 deployment web created
+ 🌐 host app.example.com linked
Reachable:
web
https://web-x7k2.unisrv.dev (base)
https://app.example.com (custom)
curl -sI https://app.example.com | head -1
HTTP/2 200
root@homelab-nucmoments later
09:14:11 Accepted provision for 0b41e6d2-93c7-4f15-8a2b-d6e0c4f71a39
09:14:12 Image ready: ghcr.io/acme/web:1.4 -> sha256:9e1f04c2a7b3… 48.2 MB in 1.2s
09:14:13 Instance 0b41e6d2-93c7-4f15-8a2b-d6e0c4f71a39 state event: StateChange(ExecutingContainer)

The CLI never names a node. Your server's log shows the app arriving.

Anything in a container. On any machine.

Declared in unisrv.hcl and applied with unisrv up, or run once from the CLI.

  • Web app

    A domain, its certificate, and the deployment it routes to.

    unisrv.hcl
    project = "unisrv-io"
    
    service "unisrv" {
      hosts = ["unisrv.io"]
    
      location "/" { deployment = "web-ssr" }
    }
    
    deployment "web-ssr" {
      port = 80
      container {
        image = "ghcr.io/unisrv/unisrv-io:latest"
      }
    }

    The whole file that deploys unisrv.io. Of course we self-host :)

  • App + database

    Postgres keeps its volume through restarts and rollouts. The api reaches it by name.

    unisrv.hcl
    deployment "api" {
      port    = 8000
      network = "internal"
      container {
        image = "ghcr.io/acme/api:1.4"
        env   = { PGHOST = "postgres" }
      }
    }
    
    deployment "postgres" {
      network = "internal"
      container {
        image = "postgres:17"
        env   = { POSTGRES_PASSWORD = var.db_password }
      }
      volume {
        mount_path = "/var/lib/postgresql/data"
        size       = "10GB"
      }
    }
    
    network "internal" {}
  • One-off task

    Runs to completion and exits with the container's code. On a node with KVM, each one is its own Firecracker microVM.

    terminal
    unisrv instance run --rm python:3.13 python3 -c "$AGENT_CODE"
    
    unisrv instance run --rm alpine echo hello
    [09:14:12] Image ready: library/alpine:latest -> sha256:d56c381f961d… 57.5 MB in 563ms
    [09:14:13] state: online
    [09:14:13] Instance v. 0.1.0
    [09:14:13] state: pulling_container_image
    [09:14:13] state: executing_container
    hello
    [09:14:13] Container exited with code: 0 after 8ms
  • Preview per PR

    The same file under another environment name is an independent copy, at its own *.unisrv.dev address.

    terminal
    # in CI, for every pull request
    unisrv up -y --env pr-42 --var tag=$GIT_SHA
    
    # once it merges
    unisrv destroy -y --env pr-42

None of these name a machine: the control plane picks one.

Your servers, ours, or both.

A node is any Linux machine running the Unisrv runtime, yours or ours. Nothing in unisrv.hcl changes when the machine does.

Bring your compute

Connect any Linux machine you have, even one behind NAT.

Use Unisrv compute

Deploy without a server of your own. Allocated per invite during the alpha.

Mix them

Run both in one environment, under one control plane.

  • fsn1-metal-01Hetzner · bare metal
    Owner: YoursIsolation: microVMCapacity: 32 vCPU · 128 GB
    Status: Active
  • homelab-nucHome · behind NAT
    Owner: YoursIsolation: microVMCapacity: 8 vCPU · 32 GB
    Status: Active
  • ec2-eun1-01AWS · EC2 m7i.xlarge
    Owner: YoursIsolation: containerCapacity: 4 vCPU · 16 GB
    Status: Active
  • gra-vps-02OVH · VPS
    Owner: YoursIsolation: containerCapacity: 4 vCPU · 8 GB
    Status: Active
  • Unisrv capacitysto1-a
    Owner: UnisrvIsolation: microVMCapacity: managed by Unisrv
    Status: Managed

One environment's nodes and what runs on each.

Our architectureA runtime and control plane with an opinionated interface.

The Unisrv runtime spawns and orchestrates containers as instances on every node, so a rented server, your own rack and Unisrv capacity behave as one platform. The control plane decides what runs where. Networking dials out, so no port forwarding is needed.

A four layer stack. Unisrv provides the top three: the interface you declare a project in, the control plane that holds desired state and places instances, and the runtime that runs them on every node. The fourth layer, the hardware underneath, is yours: bare metal, a VPS, a cloud VM, a homelab, or capacity Unisrv provides.

Desired state
unisrv up sends what you declared and the control plane acts on the difference. Kubernetes' reconciliation model, without a cluster to run.
Placement
The control plane picks a node per instance. Instances don't move, and a volume pins its replica to its node.
Isolation
Firecracker microVMs for untrusted code, plain containers on hosts that are already VMs. Same deployment either way.
Service discovery
Deployments on one internal network reach each other by name, across nodes.

No inbound ports, port forwarding or public IP.

Your node, inside your own network behind NAT, dials out to the Unisrv edge over QUIC, and requests come back down that connection, secured with mutual TLS 1.3. Your IP stays private and your nodes stay off the public internet.

Node-to-node traffic rides the same encrypted transport, and public TLS terminates at the edge.

Your compute.One control plane.

Unisrv is in closed private alpha. We are looking for alpha users.

Read the docs