Skip to content

One machine

vxnsin edited this page Aug 31, 2026 · 3 revisions

One machine

The usual setup. One warden looks after one machine and hands out ports from a single pool.

Start it

warden serve
██             ██        ██     ██  █████  ██████  ██████  ███████ ███    ██
 ███         ███         ██     ██ ██   ██ ██   ██ ██   ██ ██      ████   ██
    █████████            ██  █  ██ ███████ ██████  ██   ██ █████   ██ ██  ██
    ██     ██            ██ ███ ██ ██   ██ ██   ██ ██   ██ ██      ██  ██ ██
    █████████             ███ ███  ██   ██ ██   ██ ██████  ███████ ██   ████
█████████████████
███  █ ███ █  ███    nothing binds a port without asking
███  █ ███ █  ███
███           ███    v0.1.0  listening on http://127.0.0.1:7010
                     pool 8000-8999  1 reserved

It listens on 127.0.0.1:7010 and hands out 8000-8999. The registry port itself is reserved automatically, which is the 1 reserved above.

Leave it running. Everything else talks to it.

Claim a port

$ warden register shop-api --kind backend --project shop
8000

The only output is the port, so it drops straight into a script:

PORT=$(warden register shop-api --kind backend)
exec ./server --port "$PORT"

--kind says what the service is: backend, frontend, worker, database, cache, proxy, or anything else you like. It is what colours the dashboard and what --kind filters on. --project groups the services of one codebase.

See what is held

$ warden ls
SERVICE   KIND      PROJECT  ADDRESS         PID
shop-api  backend   shop     127.0.0.1:8000  -
shop-web  frontend  shop     127.0.0.1:8001  -

Narrow it with --project shop or --kind backend, or get machine-readable output with --json.

Look one up

Instead of hardcoding a neighbour's address:

$ warden get shop-api
127.0.0.1:8000

Give it back

warden release shop-web

Asking for a particular port

Two different wishes, two different flags:

# "3000 would be nice, but anything free will do."
warden register shop-web --kind frontend --preferred-port 3000

# "It has to be 3000, the number is baked into a config I cannot change."
warden register legacy-crm --kind backend --require-port 3000

--preferred-port quietly falls back to the pool when the port is taken, reserved or already in use. --require-port fails with a 409 instead. Both may name a port outside the pool, which is how a legacy service on 3000 joins the registry at all.

From Python

A service can ask for its own port as it starts:

import uvicorn
from warden import register

port = register("shop-api", kind="backend", project="shop")
uvicorn.run(app, port=port)

Look a neighbour up rather than hardcoding it:

from warden import WardenClient

with WardenClient() as client:
    backend = client.lookup("shop-api")
    base_url = f"http://{backend.address}"

Short-lived processes can hand the port back on the way out:

from warden import reserve

with reserve("test-fixture", kind="worker") as port:
    run_server(port)

Leases, for things that will not clean up

A registration lasts until it is released. In CI or a test fixture, nothing gets the chance:

warden register ci-runner --kind worker --ttl 600

The entry disappears ten minutes later unless something renews it. A heartbeat pushes the expiry out again:

curl -X POST localhost:7010/v1/services/ci-runner/heartbeat -d '{}' \
  -H 'content-type: application/json'

Sent without a ttl, a heartbeat renews the lease the service registered with. It can never turn a lease into a permanent registration by accident.

The dashboard

warden tui

Two live tables, two seconds apart. tab swaps between what warden handed out and what is actually listening.

Key Action
j k Move
tab Switch view
r Reload now
d Release the service, or stop the process
q Quit

Choosing a different pool

warden serve --pool 4000-4999 --reserved 4200,4500-4510

Or, so it sticks:

WARDEN_POOL_START=4000
WARDEN_POOL_END=4999
WARDEN_RESERVED=4200,4500-4510

See Configuration for the full list, including a .env file.

Keeping it running

warden is a plain HTTP server; anything that keeps a process alive will do.

Windows, Task Scheduler: a task at logon running %USERPROFILE%\.local\bin\warden.exe serve.

Linux, a user service in ~/.config/systemd/user/warden.service:

[Unit]
Description=warden

[Service]
ExecStart=%h/.local/bin/warden serve
Restart=on-failure

[Install]
WantedBy=default.target
systemctl --user enable --now warden

The registry survives restarts, so services keep the ports they had.

Clone this wiki locally