ipvsman manages Linux IPVS services from YAML drop-ins.
Every invocation must either run the long-lived service (--service) or a one-shot action (for example --test, --list-services, --status-detailed).
# Validate config ipvsman.py --test # One-shot status ipvsman.py --status-detailed --show-counters # Daemon mode (e.g. under systemd) ipvsman.py --service
# Lists ipvsman.py --list-services ipvsman.py --list-frontends ipvsman.py --list-backends ipvsman.py --list-healthchecks # Manual checks ipvsman.py --healthcheck-now ipvsman.py --healthcheck-now-group dns-core ipvsman.py --healthcheck-now-backend dns-core/10.0.0.10 # Filtered watch output ipvsman.py --status-detailed --filter-group dns-core --watch 2
| Flag | Default | Notes |
|---|---|---|
--service |
disabled | long-lived daemon; required for systemd unless using a one-shot action |
--config-dir |
/usr/local/etc/ipvsman |
config root |
--interval |
5.0 |
main loop tick seconds |
--reload-interval |
30.0 |
config reload trigger seconds |
--stats-interval |
15.0 |
live IPVS poll seconds (0 to disable) |
--lock-file |
/run/ipvsman/ipvsman.lock |
single-instance lock file |
--pid |
unset | daemon PID hint for --reload |
--check-workers |
16 |
healthcheck worker count |
--shutdown-timeout |
10.0 |
worker shutdown wait seconds |
--cold-start-sec |
30.0 |
startup grace for unknown health |
--stale-grace-sec |
30.0 |
keep weight when health is stale |
--service is mutually exclusive with one-shot actions (--test, --list-*, --status, --status-detailed, --reset, --healthcheck-now, ...).
| Flag | Default | Notes |
|---|---|---|
--api-enable |
disabled | enable HTTP API listener |
--api-host |
127.0.0.1 |
API bind host |
--api-port |
9111 |
API bind port |
--api-token |
unset | falls back to IPVSMAN_API_TOKEN |
--api-enable-write |
disabled | allows PUT /v1/config |
--api-max-body-bytes |
1048576 |
max API write payload (1 MiB) |
| Flag | Default | Notes |
|---|---|---|
--prometheus-metrics |
disabled | enable metrics endpoint |
--no-prometheus-metrics-stats |
disabled | do not export live ipvsadm --stats metrics |
--no-prometheus-metrics-healthcheks |
disabled | do not export healthcheck metrics |
--prometheus-metrics-stats-labels |
configured |
stats label mode: configured, route, or both |
--prometheus-host |
localhost |
standalone metrics bind host |
--prometheus-port |
9110 |
standalone metrics bind port |
Naming: backend resolve metrics mirror backend IP change — global ipvsman_backend_resolve_error_events_total, per-frontend ipvsman_backend_resolve_errors_total / ipvsman_backend_resolve_errors_last_timestamp_seconds. When live IPVS stats are enabled, scrape errors increment ipvsman_ipvs_stats_scrape_failures_total (counter).
Configured stats labels:
- service metrics:
group,frontend - real metrics:
group,frontend,address,backend_port(addressmaps to configured backend address/hostname when available)
Healthcheck metrics:
ipvsman_healthcheck_state(1healthy,0unhealthy,-1unknown)ipvsman_healthcheck_ready(1ready,0not ready)- Labels follow the same configured/route/both mode as stats labels.
Example alert (Prometheus):
groups: - name: ipvsman rules: - alert: IpvsmanIpvsStatsScrapeFailing expr: increase(ipvsman_ipvs_stats_scrape_failures_total[5m]) > 0 for: 5m labels: severity: warning annotations: summary: ipvsman cannot read live IPVS stats for metrics
| Flag | Default | Notes |
|---|---|---|
--output |
table |
json or table |
--watch |
disabled | refresh output every N seconds |
--show-counters |
disabled | include counters in detailed output |
--show-rates <seconds> |
disabled | include rate deltas |
--only-active |
disabled | show active rows only |
--filter-group |
unset | filter by group name |
--filter-frontend |
unset | filter by group/frontend |
--filter-backend |
unset | filter by backend IP |
--no-color |
disabled | no ANSI colors |
| Flag | Default | Notes |
|---|---|---|
--test |
disabled | validate config and exit |
--list-services |
disabled | list virtual services |
--list-frontends |
disabled | list frontends |
--list-backends |
disabled | list backends |
--list-healthchecks |
disabled | list healthcheck rows |
--status, -s |
disabled | one-shot status |
--stats, -S |
disabled | one-shot live IPVS stats (ipvsadm -ln --stats) |
--status-detailed |
disabled | detailed report |
--reset |
disabled | clear managed state |
--dump |
disabled | dump live IPVS config (ipvsadm -Sn) |
--reload |
disabled | validate config, then send SIGHUP to daemon |
--healthcheck-now |
disabled | run all checks once |
--healthcheck-now-group |
unset | run checks for one group |
--healthcheck-now-backend |
unset | run checks for one backend |
--clear-on-exit |
disabled | clear managed entries on shutdown |
--startup-full-replace |
disabled | clear first, then reconcile |
--disable-group |
unset | one-shot disable by group |
--disable-frontend |
unset | one-shot disable by group/frontend |
--disable-backend |
unset | one-shot weight=0 by group/backend_ip |
The directory passed to --config-dir (default /usr/local/etc/ipvsman) contains:
groups/*.yamlbackends/*.yamlbackend-maps/*.yamlcheck-refs/*.yaml
Path rules for backend_files:
- only relative paths are allowed
- absolute paths are rejected
- path traversal outside that config directory is rejected
- group: dns-core vip: [192.0.2.1, 198.51.100.1] scheduler: wrr frontends: - name: dns-udp proto: udp port: domain - name: dns-tcp proto: tcp port: domain - name: doh proto: tcp port: https vip: [192.0.2.2] scheduler: rr backend_map_ref: dns_pool healthcheck: type: dns query_name: health.example.com query_type: A interval: 10 timeout: 3 rise: 2 fall: 3
dns_pool: - ip: 10.0.0.10 weight: 10 port_map: "domain": 53 - ip: 10.0.0.11 weight: 10 port_map: "*": 5353 check_ref: registry-ns2
registry-ns2: ip: 192.168.1.100 port: 9999 type: http path: /status/ns2
API is off by default. Enable with --api-enable.
# API enabled (daemon) ipvsman.py --service --api-enable --api-host 127.0.0.1 --api-port 9111 # Token from env export IPVSMAN_API_TOKEN='replace-with-secret' ipvsman.py --service --api-enable
Token resolution order:
--api-tokenIPVSMAN_API_TOKEN- unset (no auth required)
Security note:
- if
--api-hostis not localhost (127.0.0.1,localhost,::1) and no token is set, startup logs anALERT
Rate limiting:
- per-IP sliding window on all API routes
- default
300 requests/minuteper client IP - overflow returns
429 {"error":"rate limit"}
Read endpoints:
GET /v1/servicesGET /v1/frontendsGET /v1/backendsGET /v1/healthchecksGET /v1/status/detailedPOST /v1/healthchecks/runGET /openapi.jsonGET /openapi.yamlGET /metrics(only when API + metrics are both enabled)
Write endpoint:
PUT /v1/config(requires--api-enable-write)- writes to
groups/api-put.yaml - reloads desired snapshot atomically
Common response codes:
401unauthorized403write disabled404not found413payload too large422validation failed429rate limit exceeded
Standalone metrics server:
ipvsman.py --service --prometheus-metrics --prometheus-host localhost --prometheus-port 9110
If API and metrics are both enabled, /metrics is served on API port (9111 by default).
Format support:
- default: Prometheus text (
text/plain; version=0.0.4) - OpenMetrics: send
Accept: application/openmetrics-text
- systemd: the role ships a static unit file that runs
ipvsman.py --service./etc/default/ipvsmanmay provide optionalIPVSMAN_API_TOKENand optionalIPVSMAN_EXTRA_ARGS. - single-instance lock prevents duplicate daemon runs
SIGHUPtriggers reload (systemctl reload ipvsman)- apply worker is single-threaded with queue coalescing
- config version is newest loaded file mtime
- live IPVS polling uses
--stats-interval - API
PUTenforces--api-max-body-bytes
cd files
PYTHONPATH=. python3 -m unittest discover -s src/test -vThis is an AI-assisted project: most implementation and routine edits were produced with Cursor agents/assistants, while design and architecture decisions remain human-driven.