-
Notifications
You must be signed in to change notification settings - Fork 0
Command line
warden introduce itself and list the commands
warden --version print the version
warden serve run the registry
warden tui open the dashboard
warden run start a program on a port warden picked
warden env claim a port and print it as environment
warden register claim a port and print it
warden get print the address of one service
warden ls list every registered service
warden release give a port back
warden reap release ports held by something that is gone
warden heartbeat push a lease out before it runs out
warden pool show how much of the pool is in use
warden history what happened to a port, or to a service
warden ports show what is listening on this machine
warden kill stop whatever is holding a port
warden nodes list the wardens this one knows about
warden doctor check everything and say what is wrong
warden service start warden with the machine
warden setup answer a few questions, once
warden settings show what is in effect, and where it came from
warden update say whether a newer warden exists, and fetch it
Every command that talks to a warden takes --url / -u and --token, both of
which also read WARDEN_URL and WARDEN_TOKEN. Every command that prints a
table also takes --json.
Two flags run through the fleet, and they mean different things:
| Flag | On | Means |
|---|---|---|
--all |
ls, pool, ports, tui
|
Ask every warden, not just this one |
--node NAME |
register, release, heartbeat
|
Do it on that one warden, through this one |
warden kill reads the local machine and ignores --url entirely. warden ports
does too, unless --all is given, which needs a hub to ask.
--install-completion teaches your shell to complete every one of them; see
Installation.
Start a program on a port warden picked, hold the port while it runs, and give it back when it exits.
$ warden run -- npm run dev
shop-api -> 8000
VITE ready, listening on http://localhost:8000Everything after -- is the command. The child gets PORT, WARDEN_PORT,
WARDEN_HOST, WARDEN_ADDRESS and WARDEN_SERVICE added to its environment —
PORT because every framework already reads it.
| Flag | Meaning |
|---|---|
--name |
Register under this name, default the current directory's |
--kind, -k
|
What the service is, default service
|
--project |
Group services of one codebase |
--host |
Interface the process will bind to |
--preferred-port |
Wish for this port, take another if it is not free |
--require-port |
Insist on this port, and fail if it is not free |
--ttl |
Hold a lease this long, renewed while the process runs |
--anyway |
Start on a free port when no warden answers |
Three things it does that a shell wrapper gets wrong:
- The child's exit code becomes warden's, so nothing swallows a failure.
- The port is released whether the program exits, crashes or is interrupted.
- The registration carries the child's process id, which is what later tells a held port from an abandoned one. See warden reap.
With no warden reachable it refuses rather than picking a port itself, because
guessing at a port is the thing warden exists to stop. --anyway overrides
that, takes a free port from the pool, and says on standard error that nothing
is registered.
The same claim, printed instead of wrapped — for an IDE run configuration, a Makefile, or anything else that cannot be started by warden.
$ warden env shop-api --kind backend
PORT=8000
WARDEN_PORT=8000
WARDEN_HOST=127.0.0.1
WARDEN_ADDRESS=127.0.0.1:8000
WARDEN_SERVICE=shop-api| Flag | Meaning |
|---|---|
--kind, -k
|
What the service is, default service
|
--project, --host, --preferred-port, --require-port
|
As warden register
|
--export |
Prefix each line with export
|
--write .env |
Set those keys in a dotenv file, leaving every other line alone |
eval $(warden env shop-api --export)
warden env shop-api --write .env--write replaces each key where it already stands and appends the rest. A
.env is usually somebody else's file too, so nothing warden did not write is
touched.
Run the registry.
| Flag | Meaning |
|---|---|
--host |
Interface to listen on |
--port |
Port to listen on |
--pool 8000-8999 |
Range of ports to hand out |
--reserved 8080,9000-9010 |
Ports never handed out |
--database |
Path to the registry database |
--no-probe |
Do not test ports for existing listeners |
Fleet settings have no flags; they are environment only. See Configuration.
Claim a port. Prints the port and nothing else, so it drops into a script.
| Flag | Meaning |
|---|---|
--kind, -k
|
Required. What the service is |
--project |
Group services of one codebase |
--host |
Interface the service will bind to, default 127.0.0.1
|
--preferred-port |
Wish for this port, take another if it is not free |
--require-port |
Insist on this port, fail if it is not free |
--ttl |
Release the port again after this many seconds |
--pid |
Process id of the service |
--node |
Register on that warden in the fleet, through this one |
--preferred-port and --require-port cannot both be given.
$ warden register shop-api --kind backend --project shop
8000Print one service's address.
$ warden get shop-api
127.0.0.1:8000Given as node/service, it asks that node through the hub:
$ warden get build-01/build-runner --url http://hub:7010
127.0.0.1:9000Exits 1 with no service registered as 'x' when it is not there.
List registered services.
| Flag | Meaning |
|---|---|
--project |
Only this project |
--kind |
Only this kind |
--holders |
Also say whether each holder is still there |
--stale |
Only the ones whose holder is gone |
--all |
Ask every warden in the fleet, not just this one |
$ warden ls --project shop
SERVICE KIND PROJECT ADDRESS PID
shop-api backend shop 127.0.0.1:8000 -
shop-web frontend shop 127.0.0.1:8001 ---all asks every warden in the fleet and adds a NODE column. Nodes that did
not answer are named on standard error, so a pipe still gets a clean table. See
Cluster.
--holders adds a HOLDER column saying whether whoever asked for each port is
still there:
$ warden ls --holders
SERVICE KIND ADDRESS PID HOLDER
shop-api backend 127.0.0.1:8000 14204 running
old-job worker 127.0.0.1:8001 9930 goneA holder is gone when the process it named no longer exists, or when nothing
is listening on its port. It costs one sweep of the machine's sockets, which is
why it is asked for rather than always done, and it cannot be combined with
--all: only the machine a service runs on can see whether it is still there.
A registration touched in the last 30 seconds is never called gone. A service that has registered and not yet bound its socket is starting, not dead.
Give a port back to the pool.
$ warden release shop-web
released shop-web--node build-01 releases it on that warden instead, through the one you asked.
Release the ports of services that are no longer there.
$ warden reap
release old-job? nothing is on 8001 and pid 9930 is gone [y/N]: y
released 1It asks per service and names why each one qualifies. --yes skips the
questions, for a machine that runs it on a schedule.
Nothing is ever reclaimed on its own. A service in the middle of a restart would lose its port to a timer, and a registry that takes ports back without being asked is worse than one holding a few dead rows.
Push a lease out before it runs out, for a service registered with a --ttl.
$ warden heartbeat ci-runner --ttl 600
127.0.0.1:9000| Flag | Meaning |
|---|---|
--ttl |
Seconds from now; without it, the lease it registered with |
--pid |
Update the recorded process id at the same time |
--node |
Do it on that warden in the fleet |
$ warden pool
8000-8999 2 allocated 997 free 1 reservedCounts only ports inside the pool. A service on a --require-port outside the
range is registered but not counted here.
--all reports every node and the fleet totals, with FREE coloured by how little
is left — the point of the view being to spot the machine about to run out:
$ warden pool --all --url http://hub:7010
NODE POOL HELD FREE RESERVED
build-01 9000-9099 4 96 1
hub 8000-8999 2 997 1
web-02 9000-9099 98 2 1The totals are a sum of what is left, never one pool the fleet shares: two nodes may hand out the same numbers on different machines.
What happened to a port, kept after it stopped being true.
$ warden history 8000
WHEN WHAT SERVICE KIND ADDRESS PID
7s ago released shop-api backend 127.0.0.1:8000 14204
2h ago registered shop-api backend 127.0.0.1:8000 14204| Argument | Meaning |
|---|---|
what |
A port, or a service name. Everything recent without one |
| Flag | Meaning |
|---|---|
--limit |
How many to show, default 20 |
Every registration, renewal, move, release and expiry is written down as it
happens, so warden history 8000 still answers for a service released weeks
ago. Rows are dropped past a cap, so a warden left running for a year does not
grow a database nobody asked for.
What is listening on this machine. Needs no server.
| Flag | Meaning |
|---|---|
--port |
Only this port |
--udp / --no-udp
|
Include UDP sockets, on by default |
--all |
Ask every warden in the fleet, and add a NODE column |
The WARDEN column names the service when the port came from the registry, which needs a warden to be reachable. Without one the column is simply blank; the rest still works.
See Ports and processes.
Stop whatever is holding a port. Needs no server.
| Argument | Meaning |
|---|---|
target |
A port, or a process id with --pid
|
| Flag | Meaning |
|---|---|
--pid |
Read the number as a process id |
--force |
Kill it outright if it will not stop politely |
--yes, -y
|
Do not ask first |
List the wardens this one knows about.
| Flag | Meaning |
|---|---|
--forget NAME |
Remove a node that is not coming back |
$ warden nodes --url http://hub:7010
NODE URL POOL VERSION STATUS LAST SEEN
build-01 http://build-01:7010 9000-9099 0.1.0 online 4s agoThe dashboard. --interval sets the refresh in seconds, default 2. --all shows
the whole fleet instead of one warden.
| Key | Action |
|---|---|
↑ ↓ j k
|
Move |
tab |
Switch between services and ports |
n |
Step through one node at a time, with --all
|
r |
Reload now |
d |
Release the service, or stop the process |
q |
Quit |
The ports view works with no warden running at all: it falls back to reading the
machine directly, says so in the subtitle, and d stops a process the same way
warden kill does. The services view still needs a registry.
0 on success, 1 on any refusal, with the reason on standard error. The
message is the same one the API would return, so a script can act on it:
if ! PORT=$(warden register shop-api --kind backend 2>/dev/null); then
echo "no port available" >&2
exit 1
fiEverything worth checking, in one command, so "why is this not working" does not mean running four others and comparing what they said.
$ warden doctor
ok warden 0.1.0 answering at http://127.0.0.1:7010, role hub
ok settings from ~/.config/warden/warden.toml
warn listening on 0.0.0.0 with no token set - anyone who can reach this
machine can hand out and release ports
ok pool 8000-8999, 4 held, 995 free
warn 2 of 6 registrations held by something that is gone - warden reap
ok build-01 online, last seen 4s ago
note could not check for updates - github is unreachable| Level | Means |
|---|---|
ok |
Checked, nothing wrong |
note |
Worth knowing, or a check that could not run at all |
warn |
Works, but somebody should look at it |
fail |
Broken |
Exits 1 only when something failed, and 0 on warnings, so it drops into a
health check without an unset token being read as the machine being down.
Have this machine start warden serve at login.
$ warden service install
systemd user unit
~/.config/systemd/user/warden.service
[Unit]
Description=warden - hands out ports and says who holds them
...
systemctl --user daemon-reload
systemctl --user enable --now warden.service
Write it? [y/N]:| Command | Does |
|---|---|
warden service install |
Show what it would write, then write it |
warden service uninstall |
Take it away again |
warden service status |
Say whether it is installed, and whether it is up |
--yes on install and uninstall skips the question.
| System | What gets written |
|---|---|
| Linux | A systemd user unit in ~/.config/systemd/user
|
| macOS | A launchd agent in ~/Library/LaunchAgents
|
| Windows | A command in the Startup folder |
Always as the account that ran it, never as root or SYSTEM: a warden started by another user reads that user's settings and hands out ports from a registry nobody else can see.
On Windows it is a Startup command rather than a scheduled task, because
creating a task is refused outright on plenty of managed machines. That also
means there is no service manager to ask, so status says running when a
warden is answering on the configured port, whoever started it.
Asks the handful of questions that matter and writes the answers to the config file. Enter accepts every default, and it keeps any setting it did not ask about. See Configuration.
Every setting, its value, and which source that value came from.
warden settings # the table
warden settings --json # the same as data
warden settings set pool_end 4199 # write one to the config file
warden settings unset probe # take it back outset types and checks the value before writing it, so the file never holds
something warden would refuse to start with, and it says so when the environment
will override what you just wrote anyway.
warden — nothing binds a port without asking ·
uv tool install warden-ports
Repository · Issues · Releases · PyPI · MIT
Getting started
While it runs
Several machines
Reference