Nodes
A katana configuration file serves one node for each [[node]] block it contains. The block says which panel to ask and which node ID to ask for. It also covers the parts of the node the panel does not decide: the address to listen on, how often to poll, the certificate, and a few switches. The panel supplies everything else, including the port, transport, TLS setting, cipher and users.
This page describes every key of [[node]], [node.api], [node.controller] and [node.controller.cert]. It also shows how several nodes share one process, how a hot reload decides whether an edited block is still the same node, and what happens when a node cannot start. Routing ([node.route]) and the Hysteria 2 table ([node.hysteria]) have their own pages, linked at the end.
A complete node
Section titled “A complete node”Both examples serve one TLS node with a certificate you manage yourself. Pick the tab for your panel.
[[node]]panel_type = "NewV2board"
[node.api]host = "https://panel.example.com"node_id = 1key = "replace-with-the-panel-key"node_type = "V2ray"enable_vless = true # serve VLESS; leave false for VMesstimeout = 10
[node.controller]listen_ip = "0.0.0.0"update_periodic = 60
[node.controller.cert]mode = "file"cert_file = "/etc/katana/fullchain.pem"key_file = "/etc/katana/privkey.pem"[[node]]panel_type = "SSpanel"
[node.api]host = "https://panel.example.com"node_id = 1key = "replace-with-the-panel-key"node_type = "Trojan"timeout = 10
[node.controller]listen_ip = "0.0.0.0"update_periodic = 60
[node.controller.cert]mode = "file"cert_file = "/etc/katana/fullchain.pem"key_file = "/etc/katana/privkey.pem"Check the file before you start katana:
katana --test -c /etc/katana/config.tomlConfiguration OK--test catches unknown keys, wrong types, an unknown panel_type or node_type, and bad routing rules. It sends no request to the panel and binds nothing. For any node type except Hysteria 2 it does not read the certificate files. A wrong panel URL or key, a bad listen_ip or a missing certificate therefore passes --test and shows up only when the node starts, as an error that the node retries until you fix the cause. When a node fails to start covers those errors.
The node block
Section titled “The node block”[[node]] is an array of tables: write one block for each node, each with its own sub-tables. Only panel_type sits directly in the block.
| Key | Type | Required | Default | Description |
|---|---|---|---|---|
panel_type | string (enum) | yes | — | Which panel API this node talks to: NewV2board (UniProxy, for Xboard and V2board), its alias V2board, or SSpanel (mod_mu). Compared case-insensitively. Leaving it out fails --test with unknown panel_type ""; any other value fails the same way, with the value shown lowercased. At startup, such a node is logged and skipped, and katana exits only when no node can be built. On a hot reload, an unknown value makes katana refuse the whole reload and keep running the current configuration. Part of the node identity. |
api | table | yes | — | How to reach the panel and what to ask it for, written as [node.api]: the panel URL, node ID, key and node type, plus a few local overrides. |
controller | table | no | {} | This machine's side of the node, written as [node.controller]: listen address, poll interval, the sniffing, rule and upload switches, and the [node.controller.cert] certificate table. Every key has a default. |
route | table | no | {} | This node's routing table, written as [node.route] with [[node.route.rule]] blocks. Without it, every flow goes to the direct outbound. |
hysteria | table | no | {} | Settings for a Hysteria 2 node that no panel describes, written as [node.hysteria]: credential format, UDP relay, masquerade, and optionally a local port and obfuscation. Ignored unless node_type is a Hysteria 2 value. |
Every table in the block rejects unknown keys, so a typo stops --test and startup instead of being ignored:
configuration error: config parse error: TOML parse error at line 5, column 1 |5 | apihost = "x" | ^^^^^^^unknown field `apihost`, expected one of `host`, `node_id`, `key`, `node_type`, `enable_vless`, `vless_flow`, `timeout`, `speed_limit`, `device_limit`, `rule_list_path`, `disable_custom_config`Panel connection
Section titled “Panel connection”[node.api] tells katana where the panel is, how to authenticate, and which node to ask for. It also holds three local overrides: enable_vless, speed_limit and rule_list_path.
| Key | Type | Required | Default | Description |
|---|---|---|---|---|
host | string | yes | — | Base URL of the panel, with the scheme: https://panel.example.com. If the panel is served under a path, include it. Trailing slashes are removed before katana appends the API path. --test does not check the value: a host without https:// or http:// passes, and then every panel request fails. Part of the node identity, compared exactly as written. |
node_id | u32 | yes | — | The node's ID in the panel. A negative value fails the parse with an invalid value error ending in expected u32. Part of the node identity. |
key | string | yes | — | The panel's node communication key: the server token in Xboard and V2board, muKey in SSPanel. UniProxy panels receive it as the token query parameter, SSPanel as both key and muKey, on every request. Part of the node identity. |
node_type | string (enum) | yes | — | What kind of node the panel describes, compared case-insensitively: V2ray, vmess or vless (all three mean a V2ray node, VMess unless enable_vless is on), Trojan, Shadowsocks, or Hysteria2, hysteria or hy2. UniProxy panels receive it lowercased as the node_type query parameter (or as vless for a V2ray node with enable_vless on), so write it the way the panel spells it. Leaving it out fails with unknown node_type "". On NewV2board and V2board, the type katana asks for is part of the node identity, so a hot reload that changes the requested type (anything but case) replaces the node. Any other change, and any change on SSPanel, builds a new panel client on a hot reload and applies at once. |
enable_vless | bool | no | false | Serve VLESS instead of VMess on a V2ray node. A UniProxy panel is then asked for node_type=vless, and katana reads the transport settings from the panel's network_settings object instead of networkSettings. SSPanel can also turn VLESS on from custom_config; either source is enough. On NewV2board and V2board, turning it on or off for a node whose node_type is V2ray or vmess changes the type katana asks for, which is part of the node identity, so a hot reload replaces the node. With node_type = "vless" the type asked for is vless either way. Otherwise, including on SSPanel, a hot reload that changes it builds a new panel client and rebuilds the listener at once. |
vless_flow | string | no | "" | SSPanel legacy server string only: the VLESS flow of a V2ray node. Any non-empty value makes such a node fail with node requests kernel-unsupported feature: VLESS XTLS flow, so leave it empty. UniProxy panels and SSPanel custom_config supply the flow themselves. A hot reload that changes it builds a new panel client and applies the change at once. |
timeout | u64 | no | 0 | Limit for each panel HTTP request, in seconds, from connecting until the whole response has been read. 0 means 5 seconds. A hot reload that changes it builds a new panel client in place, without replacing the node. |
speed_limit | float | no | 0 | Speed-limit override in Mbps (1 Mbps is 125 000 bytes per second; fractions and integers are both accepted). Above 0, it replaces every limit the panel sends for this node, node-level and per-user, so every user gets exactly this rate. 0 or a negative value uses the panel's limits. A hot reload that changes it builds a new panel client and keeps every connection; the new rate applies to every flow opened after the reload. |
device_limit | integer | no | 0 | Accepted and ignored. katana does not limit devices per user. |
rule_list_path | path | no | "" | A local file of audit rules, one regular expression per line; blank lines and lines starting with # are skipped. Each rule is matched against the destination domain or IP, without the port, and a match refuses the flow. Read together with the panel rules, so disable_get_rule = true also turns this file off. Re-read at every rule refresh, so edits to its contents apply at the next cycle (with SSPanel, only when the panel's own rule request succeeds with a full answer). An unreadable file or an invalid line is logged and skipped. Hits on these rules refuse the flow but are not reported to the panel. A hot reload that changes the path builds a new panel client, which reads the new file at once; connections are kept. |
disable_custom_config | bool | no | false | SSPanel only. Read the node from the legacy server string even when the panel is version 2021.11 or later and offers custom_config. A panel that reports an older version, or none, always gets the server string parse. A hot reload that changes it builds a new panel client and applies the change at once. |
The panel URL
Section titled “The panel URL”host is the base URL. katana removes trailing slashes and appends the API path, so https://panel.example.com/ and https://panel.example.com reach the same URLs. If the panel runs under a path, include it: https://example.com/panel makes katana request https://example.com/panel/api/v1/server/UniProxy/config.
Always include the scheme. --test accepts host = "panel.example.com", but every request built from it fails, so the node never comes up and keeps retrying its start.
How the key and node ID reach the panel
Section titled “How the key and node ID reach the panel”katana sends the key in the query string of every request, under the parameter names each panel expects.
| Panel | Node settings request | Other requests |
|---|---|---|
NewV2board / V2board |
GET /api/v1/server/UniProxy/config?node_id=…&node_type=…&token=… |
Same three parameters on …/user and …/push |
SSpanel |
GET /mod_mu/nodes/<node_id>/info?key=…&muKey=… |
key and muKey, plus node_id on the user list and on reports |
A UniProxy panel looks a node up by its ID and its type together. katana sends node_type lowercased, except that a V2ray node with enable_vless = true is sent as vless. For the node types katana serves, Xboard knows vmess, vless, trojan, shadowsocks and hysteria, plus the aliases v2ray and hysteria2, so:
- for a VLESS node, set
enable_vless = true.node_type = "vless"alone asks the panel for the VLESS node but serves VMess on it; - for a Hysteria 2 node, write
Hysteria2orhysteria. katana also acceptshy2, but Xboard does not recognise it.
SSPanel finds a node by its ID alone. katana does not serve Shadowsocks nodes from SSPanel: it accepts node_type = "Shadowsocks" with SSpanel, but the node never comes up: every start attempt fails as soon as it fetches its settings, with sspanel: Shadowsocks node type is not supported.
The per-panel pages list every field katana reads from each panel.
Speed-limit override
Section titled “Speed-limit override”speed_limit is in megabits per second, and katana converts 1 Mbps to 125 000 bytes per second. When it is above 0, it replaces every limit the panel sends for this node. When it is 0 or negative, katana uses the panel’s own limits.
speed_limit |
Panel limits | Each user’s rate |
|---|---|---|
0 |
User limit only (UniProxy) | The user’s limit, or unlimited if it is 0 |
0 |
Node limit and user limit (SSPanel) | The smaller of the two non-zero limits, or unlimited if both are 0 |
50 |
Any | 50 Mbps (6 250 000 bytes per second), whatever the panel says |
The override is not a total for the node: it applies to each user separately. Speed limits explains how the limit is enforced.
Local audit rules
Section titled “Local audit rules”rule_list_path names a text file of regular expressions, one per line. katana matches each rule against the destination a flow asks for, a domain or an IP address without the port, and refuses the flow on the first match. The rules are unanchored, so example\.com also matches example.com.example.net; write (^|\.)example\.com$ to match a domain and its subdomains only.
# One regular expression per line. Blank lines and lines starting with # are skipped.(^|\.)tracker\.example\.com$^203\.0\.113\.katana reads the file in the same refresh that fetches the panel’s rules, so disable_get_rule = true turns the file off as well. With a UniProxy panel, katana re-reads the file on every cycle, so an edit to its contents applies at the next cycle. With SSPanel, katana re-reads it only when the panel’s own rule request succeeds with a full answer; when that request fails or answers 304 Not Modified, the rules already loaded stay in place. A line that is not a valid regular expression is logged and skipped. Hits on local rules refuse the flow but are never reported to the panel. Destination audit covers panel rules, hit reporting and refresh timing.
SSPanel-only keys
Section titled “SSPanel-only keys”Two keys matter only for panel_type = "SSpanel":
disable_custom_config = truemakes katana parse the node from the legacyserverstring even when the panel offerscustom_config. A panel that reports a version older than 2021.11, or no version at all, always gets the legacy parse.vless_flowfeeds the VLESS flow for the legacy parse of a V2ray node. katana has no XTLS, so leave it empty: any value makes such a node fail withnode requests kernel-unsupported feature: VLESS XTLS flow. Withcustom_config, katana ignores this key and takes the flow from the panel.
UniProxy panels ignore both keys.
Listener and poll cycle
Section titled “Listener and poll cycle”[node.controller] holds the settings that belong to this machine: where to listen, how often to talk to the panel, and three switches.
| Key | Type | Required | Default | Description |
|---|---|---|---|---|
listen_ip | string | no | "0.0.0.0" | Address the listener binds: TCP for VMess, VLESS, Trojan and Shadowsocks nodes, UDP for Hysteria 2. The port always comes from the panel (or from [node.hysteria].port). Write an IPv4 or IPv6 address without brackets, such as "0.0.0.0", "::" or "203.0.113.10". --test does not check it; a bad address makes the bind fail when the node starts. Also part of the node tag, such as V2ray_0.0.0.0_443. A hot reload that changes it rebuilds the listener. |
send_ip | string | no | "0.0.0.0" | Accepted and ignored. katana does not bind outgoing connections to a source address; the operating system chooses it. |
update_periodic | u64 | no | 60 | Seconds between poll cycles. One timer drives everything: fetching the node settings and users, refreshing the audit rules, and reporting traffic and audit hits. 0 counts as 1. The panel's own push and pull intervals are not read. The first cycle runs one full period after the node is up. It also caps the wait between attempts while a node retries its first start, which otherwise grows to 60 seconds. A hot reload that changes it restarts the timer. |
disable_upload_traffic | bool | no | false | Stop reporting user traffic to the panel. Speed limits and audit rules still apply. Read on every cycle, so a hot reload applies it at the next cycle. |
disable_get_rule | bool | no | false | Stop fetching audit rules, both from the panel and from rule_list_path. Set at startup, the node runs with no audit rules. Turned on by hot reload, it stops the refreshes but keeps the rules already loaded until katana restarts. Audit hits already recorded are still reported. |
disable_sniffing | bool | no | false | Stop reading the TLS SNI or HTTP Host from the first bytes of each TCP flow. Sniffing only feeds the routing rules, so a flow addressed by IP can still match a domain rule; the destination katana connects to is never changed. A hot reload that changes it rebuilds the listener at once, dropping the node's connections. |
cert | table | depends | {} | The listener certificate, written as [node.controller.cert]. Required with mode = "file" for Trojan and Hysteria 2 nodes, and for V2ray nodes the panel marks as TLS. A hot reload that changes any of its keys rebuilds the listener. |
What one cycle does
Section titled “What one cycle does”At startup, a node fetches its settings and users from the panel, builds and binds its listener, and loads its audit rules. If any of that fails, the node tries again until it succeeds (When a node fails to start). Once the node is up, a single timer ticks every update_periodic seconds. Each tick runs one cycle, in this order:
- Fetch the node settings and the user list. A failed fetch keeps the last answer that worked.
- Apply the changes. A new user set is swapped in place. A new port, transport or TLS setting rebuilds the listener. An empty user list closes the listener until users return.
- Refresh the audit rules, unless
disable_get_rule = true. - Report each user’s traffic, unless
disable_upload_traffic = true. - Report audit hits (SSPanel only; UniProxy has no endpoint for them).
The first cycle runs one full period after the node is up, not immediately. A config edit that katana accepts and that touches [node.api] or a listener setting (listen_ip, [node.controller.cert], disable_sniffing, [node.hysteria] or [node.route]) also runs one cycle at once, without moving the timer; What an edit to a running node does lists the details. With the default of 60, a user you add in the panel can take up to a minute to be admitted, and traffic reaches the panel in batches of up to a minute. A shorter period makes changes arrive sooner and sends more requests to the panel. katana does not read the push and pull intervals a panel publishes.
When katana stops on SIGINT or SIGTERM, each node closes its listener and then reports its remaining traffic and audit hits once more.
Listen address and node tag
Section titled “Listen address and node tag”listen_ip is the address only; the port always comes from the panel. katana binds TCP for VMess, VLESS, Trojan and Shadowsocks nodes, and UDP for Hysteria 2. "::" listens on IPv6 and, with Linux’s default net.ipv6.bindv6only = 0, on IPv4 as well.
katana names each running node with a tag built from the node type, the listen address and the port: V2ray_0.0.0.0_443, Trojan_203.0.113.10_8443, Shadowsocks_::_10086 or Hysteria2_0.0.0.0_443. A VLESS node’s tag also starts with V2ray. The tag keys the node’s audit rules and appears in a few log lines, for example:
WARN katana::manager::proxy: node V2ray_0.0.0.0_443: 250 inbound handshake failures in the last 1s (possible handshake scan/DoS or misconfigured clients)Most other node log lines use the node ID instead: node 1: listening on 0.0.0.0:443.
Sniffing
Section titled “Sniffing”By default, katana reads the TLS SNI or HTTP Host from the first bytes of each TCP flow and passes it to the routing rules. A client that connects to an IP address can then still match a domain_suffix or geosite rule. Sniffing never changes where katana connects: the flow still goes to the address the client asked for. UDP packets are not sniffed.
Set disable_sniffing = true if your routing rules should see only the destination the client named. Audit rules always match the requested destination, with or without sniffing.
Certificates
Section titled “Certificates”[node.controller.cert] gives the listener its certificate. katana never uses the certificate settings a panel publishes and has no ACME client, so you obtain and renew certificates yourself and point katana at the files.
| Key | Type | Required | Default | Description |
|---|---|---|---|---|
mode | string (enum) | depends | "none" | "none" (no certificate) or "file" (read cert_file and key_file). Case-sensitive. "file" is required for every node that uses TLS; without it a stream node fails with TLS node requires cert.mode = "file" and a Hysteria 2 node with hysteria2 node requires cert.mode = "file". The ACME modes "dns", "http" and "tls" are refused on every stream node, TLS or not: node requests kernel-unsupported feature: ACME cert mode "dns"; on a Hysteria 2 node they fail like any value other than "file". Any other value behaves like "none" on a stream node. |
cert_file | path | depends | "" | PEM certificate chain: the server certificate first, then any intermediates (a fullchain.pem). Required with mode = "file"; if it or key_file is empty, a TLS or Hysteria 2 node fails with TLS node requires cert.cert_file and cert.key_file. Read each time the listener is built, not when the file changes. A missing file fails with the bare OS error, such as No such file or directory (os error 2), which does not name the path. |
key_file | path | depends | "" | PEM private key matching cert_file. Required with mode = "file". Read at the same moments as cert_file; a key that does not match the certificate fails the listener build. |
reject_unknown_sni | bool | no | false | Not implemented, so true is refused rather than silently ignored: the node fails with node requests kernel-unsupported feature: cert.reject_unknown_sni. Leave it false. |
Which nodes need mode = "file":
| Node | TLS | Needs a certificate |
|---|---|---|
| V2ray (VMess or VLESS) | As the panel says: UniProxy tls = 1; SSPanel security = "tls" or "xtls" in custom_config, or tls in the legacy server string. UniProxy tls = 2 (REALITY) is refused |
Only when the panel turns TLS on |
| Trojan | Always | Yes |
| Shadowsocks | Never | No; leave mode = "none" |
| Hysteria 2 | Always, inside QUIC | Yes |
katana checks the certificate settings when it builds the listener: at the node’s first start, and at every rebuild after that. For a Hysteria 2 node, --test checks them as well; for the other types it does not. A TLS stream listener accepts TLS 1.2 and 1.3; QUIC always uses TLS 1.3.
Running several nodes
Section titled “Running several nodes”One katana process can serve any number of nodes. Each [[node]] block gets its own panel client, listener, user table, traffic counters, audit rules and routing table. All nodes share the [[outbound]] pool and the [dns] resolver.
This file serves two nodes of the same panel from one machine, each on its own address:
[[node]]panel_type = "NewV2board"
[node.api]host = "https://panel.example.com"node_id = 1key = "replace-with-the-panel-key"node_type = "V2ray"
[node.controller]listen_ip = "203.0.113.10"
[node.controller.cert]mode = "file"cert_file = "/etc/katana/fullchain.pem"key_file = "/etc/katana/privkey.pem"
[[node]]panel_type = "NewV2board"
[node.api]host = "https://panel.example.com"node_id = 2key = "replace-with-the-panel-key"node_type = "Trojan"speed_limit = 50
[node.controller]listen_ip = "203.0.113.11"disable_sniffing = true
[node.controller.cert]mode = "file"cert_file = "/etc/katana/fullchain.pem"key_file = "/etc/katana/privkey.pem"Keep these rules in mind:
- Ports come from the panel. Two TCP nodes, or two Hysteria 2 nodes, can share a port only if they listen on different specific addresses;
0.0.0.0and::overlap with every address. When two nodes collide, the one that binds second fails withAddress already in use (os error 98). A TCP node and a Hysteria 2 node can use the same port number, because one binds TCP and the other UDP. - Nodes fail independently. A node that cannot start does not stop the others, and katana keeps running while that node retries.
- Give each block a distinct identity. Two blocks with the same identity (see below) both start, but a reload cannot tell them apart and applies every edit to the first one.
Node identity and reload
Section titled “Node identity and reload”katana watches its configuration file and applies edits without a restart. To do that, it has to decide whether each [[node]] in the new file is a node that is already running. It matches on five values, the node identity. Together they name one node in the panel:
| Part | Compared as |
|---|---|
panel_type |
Case-insensitive, so NewV2board and newv2board are the same node. NewV2board and V2board are different values |
[node.api].host |
Exactly as written, so adding a trailing slash counts as a change |
[node.api].node_id |
Number |
[node.api].key |
Exactly as written |
| The node type katana asks the panel for | NewV2board / V2board: vless for a V2ray, Vmess or Vless node with enable_vless = true, otherwise node_type lowercased. SSpanel has none, because it finds a node by its ID alone |
A UniProxy panel finds a node by its ID and the type it is asked for, so the same ID asked as vmess and as vless is two nodes there, each with its own users and traffic. That is why the requested type is part of the identity on those panels.
What the match means:
- Same identity: it is the same node. It keeps its traffic counters and audit state, and receives the other edits in place. An edit to
[node.api]gives it a new panel client; some edits rebuild its listener; the rest do not touch connections at all. - Identity gone from the file: katana stops the node. It closes the listener, which drops its connections, and reports the node’s remaining traffic.
- New identity: katana starts a new node from scratch, exactly as at startup.
Changing any identity field is therefore a remove followed by an add. The old node stops first, so the new one can bind the same port. The log shows each step, naming the node by panel type, host, ID and, on UniProxy panels, the requested type (never the key). Turning on enable_vless for a V2ray node on Xboard, for example, logs:
INFO katana::runtime: reload: removing node newv2board@https://panel.example.com#1/v2rayINFO katana::runtime: reload: added node newv2board@https://panel.example.com#1/vlessThe name leaves out only the key, so after a key change both lines show the same name. An SSPanel node’s name has no type: sspanel@https://panel.example.com#1. An edit that keeps the identity logs reload: reconfigured node … instead.
What an edit to a running node does
Section titled “What an edit to a running node does”| You change | Effect on a running node |
|---|---|
panel_type (other than its case), host, node_id or key; on NewV2board / V2board, also a node_type or enable_vless change that changes the requested type |
The node is replaced: stopped, then started from scratch |
listen_ip, disable_sniffing, or any key in [node.controller.cert] |
The listener is rebuilt at once, dropping every connection on the node |
node_type, enable_vless, vless_flow, speed_limit, rule_list_path, disable_custom_config or timeout, when the node is not replaced |
Applied at once: katana builds a new panel client, which reads the node settings and users in full. Connections drop only if that answer changes the protocol or transport. enable_vless also rebuilds the listener |
update_periodic |
The timer restarts; the next cycle runs one full new period later |
disable_upload_traffic or disable_get_rule |
Applied from the next cycle |
send_ip or device_limit |
Nothing; katana does not use them |
An edit in the second or third row also runs one poll cycle right away, without waiting for the timer: the node asks the panel with the new values, applies the answer (rebuilding the listener where the row says so), refreshes its audit rules and reports traffic. A speed_limit or rule_list_path edit keeps every connection: the new rates and rules apply to every flow opened after the edit, including those of users already connected, while flows already open keep their old rate. On SSPanel, node_type and enable_vless are not part of the identity, so an edit to them is always applied in place. A node that is still retrying its first start stores the edit and tries again at once with the new values.
katana checks an edit before it applies any of it:
- If an edited block’s new panel client does not build, for example because of an unknown
panel_typeornode_type, katana refuses the whole reload and every node keeps running as it was. The same applies to a new or replaced node whose routing rules do not compile. The log names the node:reload: node <name>: <error>; keeping current config. - If a running node’s new
[node.route]does not compile, that node logsnode <id>: config edit refused, keeping the running one: …and keeps its whole previous configuration, panel client and listener included.
[node.route] and [node.hysteria] edits are covered on their own pages, and Hot reload covers the rest of the file.
When a node fails to start
Section titled “When a node fails to start”A node’s first start has four steps: fetch the node settings, fetch the users, build and bind the listener, and load the audit rules. No failure ends the node. When one of the first three steps fails, the node logs the reason at ERROR, waits, and starts again from the first step. The wait is 1 second after the first failure and doubles after each one, up to 60 seconds, or up to update_periodic if that is shorter. An edit to the node’s [[node]] block cuts the wait short: the node applies it and tries again at once, because the edit may well be the fix.
stateDiagram-v2 [*] --> Starting: katana starts, or a reload adds the node Starting --> Serving: settings, users and listener up, or no users yet Starting --> Down: a fetch fails, port 0, or the listener fails Down --> Starting: the wait ends, or an edit reaches the node Serving --> Serving: poll cycle every update_periodic seconds Serving --> Stopped: SIGINT, SIGTERM, or removed by a reload Down --> Stopped: SIGINT, SIGTERM, or removed by a reload Stopped --> [*]
Each failed attempt logs one line, node <id>: <reason>; retrying in <N>s, where <N> is the wait before the next attempt:
| Step | What goes wrong | Log line (level ERROR) |
|---|---|---|
| Fetch node settings | Panel unreachable, a timeout, or an HTTP error status, such as a wrong key or an unknown node ID on Xboard | node 1: node_info failed: GET /api/v1/server/UniProxy/config; retrying in 1s (SSPanel: GET /mod_mu/nodes/1/info) |
| Fetch node settings | The answer is not the JSON katana expects, for example an HTML page served with status 200 because host points at the wrong site |
node 1: node_info failed: parse UniProxy config response; retrying in 1s (SSPanel: parse /mod_mu/nodes/1/info) |
| Fetch node settings | The JSON describes a node katana cannot use | node 1: node_info failed: newV2board: server port must be > 0; retrying in 1s, node 1: node_info failed: sspanel: Shadowsocks node type is not supported; retrying in 1s, node 1: node_info failed: /mod_mu/nodes/1/info: panel returned ret=0; retrying in 1s, … |
| Fetch node settings | The panel answers 304 Not Modified although katana asked without a cached tag |
node 1: panel returned no node info; retrying in 1s |
| Fetch node settings | SSPanel gives port 0 (a UniProxy port 0 fails with server port must be > 0 instead) |
node 1: panel returned port 0; retrying in 1s |
| Fetch users | The same request errors as for the node settings, on the user-list request | node 1: user_list failed: …; retrying in 1s |
| Fetch users | The panel answers 304 Not Modified although katana asked without a cached tag |
node 1: panel returned no user list; retrying in 1s |
| Build the listener | A refused feature, a certificate problem, or a failed bind | node 1: initial start failed: node requests kernel-unsupported feature: REALITY; retrying in 1s, … TLS node requires cert.mode = "file"; …, … No such file or directory (os error 2); …, … Address already in use (os error 98); … |
Both fetches are required: a node does not come up until it has its settings and its user list, so a failed user-list fetch is retried like any other failed step. The number after retrying in grows with each failure: 1s, 2s, 4s, and so on up to the cap. A transient cause, such as a panel that is restarting, DNS that is not answering yet, or a port still held by the process this katana replaces, therefore clears on its own. For a cause in the configuration, fix it and save the file; for one outside it, such as a missing certificate file, fix it and the node comes up at its next attempt. Either way, you do not need to restart katana. Until the node comes up, it binds nothing and reports no traffic. katana keeps serving its other nodes. If the failing node is the only one, the process runs with nothing listening, so a service manager still reports it as active; watch for these log lines.
Once a node has started, failures are not final either:
- A failed fetch during a poll logs a warning, such as
node 1: node_info: GET /api/v1/server/UniProxy/config, and the node keeps the last settings and users that worked. - A poll that returns port 0 logs
node 1: refreshed port is 0, keeping the last oneatERROR. The node keeps its last settings, and its users still follow the panel. - A failed listener rebuild logs
node 1: rebuild failed: …and leaves the node without a listener. The next cycle tries again. - A node with no users binds nothing, which is not an error, whether the list is empty at the first start or becomes empty later. The node counts as up, and binds its listener at the first cycle that brings users. Listener errors, such as a missing certificate, show up only at that point, as
rebuild failed, and are retried on every cycle.
Finding the real cause of a panel error
Section titled “Finding the real cause of a panel error”A failed panel request logs only what katana was doing, such as GET /api/v1/server/UniProxy/config or parse UniProxy config response, and not the underlying reason. Send the same request by hand from the node’s machine to see the status and body:
curl -sS -H 'Accept: application/json' -w '\nHTTP %{http_code}\n' \ 'https://panel.example.com/api/v1/server/UniProxy/config?node_id=1&node_type=vmess&token=replace-with-the-panel-key'Use the node_type katana sends: your value lowercased, or vless when a V2ray, Vmess or Vless node has enable_vless = true. Xboard answers every error with JSON and an error status, which katana logs as the failed GET:
| Status | Message | Cause |
|---|---|---|
422 |
Invalid token |
key does not match the panel’s server token |
422 |
Invalid node type specified |
A node_type Xboard does not know, such as hy2 |
400 |
Server does not exist |
No node with this ID and type |
curl -sS -w '\nHTTP %{http_code}\n' \ 'https://panel.example.com/mod_mu/nodes/1/info?key=replace-with-the-panel-key&muKey=replace-with-the-panel-key'A usable answer is JSON with "ret": 1.
The command puts the key in your shell history. Clear that entry afterwards if the machine is shared.
Common errors
Section titled “Common errors”| Message | Cause and fix |
|---|---|
configuration error: unknown panel_type "" |
panel_type is missing. Add panel_type = "NewV2board" or "SSpanel" to the [[node]] block, not to [node.api]. |
configuration error: unknown panel_type "xboard" |
Use NewV2board (or V2board) for Xboard. |
configuration error: unknown node_type "" |
[node.api].node_type is missing. |
configuration error: unknown node_type "vmess2" |
Use one of the values in the node_type row above. |
configuration error: config defines no [[node]] entries |
The file has no [[node]] block. |
configuration error: node 1: hysteria2 node requires cert.mode = "file" |
A Hysteria 2 node needs a certificate. Set mode = "file", cert_file and key_file. |
node 1: initial start failed: TLS node requires cert.mode = "file"; retrying in <N>s |
The node is a Trojan node, or the panel marks it as TLS. Add the certificate table with mode = "file". |
node 1: initial start failed: TLS node requires cert.cert_file and cert.key_file; retrying in <N>s |
mode = "file" is set but a path is empty. |
node 1: initial start failed: node requests kernel-unsupported feature: ACME cert mode "http"; retrying in <N>s |
katana has no ACME client. Use mode = "file" with certificates you renew yourself. |
node 1: initial start failed: node requests kernel-unsupported feature: VLESS XTLS flow; retrying in <N>s |
Remove the flow in the panel, or empty vless_flow for an SSPanel legacy node. |
node 1: initial start failed: Address already in use (os error 98); retrying in <N>s |
Another node or program holds the port on that address. Change listen_ip or the panel’s port. |
node 1: initial start failed: Cannot assign requested address (os error 99); retrying in <N>s |
listen_ip is not an address of this machine. |
The node keeps retrying after each initial start failed line. Once you fix the cause, it comes up at its next attempt, or at once if the fix is an edit to its [[node]] block, without a restart. Troubleshooting collects the errors from every part of katana.