Skip to content

Quick start

This page takes you from an empty server to a katana node that serves real users. You create a VMess node in an Xboard panel (any newV2board-compatible panel works the same way), put a TLS certificate on the server, write a short config file, check it, start katana, and confirm that the panel receives traffic figures.

katana and the panel split the work. The panel decides what the node is: its protocol, port, transport, WebSocket path, whether TLS is on, and which users may connect. The config file says which panel and node katana serves, where the certificate is, and which address to listen on. So most of the node’s shape is set in the panel, and the config file stays short.

sequenceDiagram
    participant P as Panel
    participant K as katana
    participant C as Client
    K->>P: GET node config (port, ws path, TLS)
    K->>P: GET user list
    Note over K: bind the TCP port
    C->>K: VMess over WebSocket + TLS
    loop every update_periodic seconds
        K->>P: GET node config and user list
        K->>P: POST traffic per user
    end

You need:

  • A Linux server with a public address, and root access or another way to bind port 443 (see step 6).
  • A domain name that resolves to the server, for the certificate. This page uses proxy.example.com.
  • Administrator access to the panel. This page uses Xboard. V2board and other panels that speak the newV2board UniProxy API work the same way. SSPanel uses different keys, covered in SSPanel.
  • Open ports. TCP 443 for clients, and TCP 80 while you obtain a certificate with an HTTP challenge. The server must also be able to reach the panel over HTTPS.
  1. Create the node in the panel. In Xboard, add a VMess node with these settings. The labels in the Xboard admin screens can differ from the descriptions below, so the table also gives the name of the field Xboard stores:

    Panel setting (Xboard field) Value on this page What katana does with it
    The address clients connect to (host) proxy.example.com Nothing. Clients use it.
    The port clients connect to (port) 443 Nothing. Clients use it.
    The port the server listens on (server_port) 443 Binds this TCP port.
    TLS on Wraps the listener in TLS with your certificate.
    Transport (network) ws Serves VMess over WebSocket.
    WebSocket path /ws Accepts WebSocket upgrades on exactly this path, and answers any other path with 404.
    Groups (group_ids) at least one Users in these groups are the node’s users.

    Xboard keeps two ports per node. katana binds server_port; port only goes into subscriptions. Set both to 443 unless something in front of katana forwards one port to another.

    Then write down two values for the config file:

    • the node ID, shown in the panel’s node list;
    • the communication key (Xboard stores it as the server_token setting). It is one key for the whole panel, shared by every node.

    Make sure at least one user can use the node. Xboard sends a user to the node only if the user is in one of the node’s groups, is not banned, has not expired, and has traffic left. katana binds nothing while the list is empty.

  2. Install katana. Follow Install, then check the binary:

    Terminal window
    katana --version
    katana 3.0.1
  3. Get a certificate. katana has no ACME client. Its only certificate mode is file: you give it a PEM certificate and a PEM private key, and it reads them from disk when it starts the listener. Any tool that produces those files works.

    Terminal window
    sudo certbot certonly --standalone -d proxy.example.com
    sudo install -d -m 700 /etc/katana/cert
    sudo install -m 644 /etc/letsencrypt/live/proxy.example.com/fullchain.pem /etc/katana/cert/fullchain.pem
    sudo install -m 600 /etc/letsencrypt/live/proxy.example.com/privkey.pem /etc/katana/cert/privkey.pem

    --standalone answers the challenge on TCP port 80, so nothing else may be listening there while it runs.

    What katana requires of the files:

    • cert_file holds the server certificate first, then any intermediate certificates. A fullchain.pem has this order.
    • key_file holds the matching private key in PEM form (PKCS#8, RSA or EC). katana refuses a key that does not match the certificate.
    • katana does not check the certificate’s names or expiry date. Clients do, so the certificate must cover the name clients use as the TLS server name.
  4. Write the config. Save this as /etc/katana/config.toml, with your panel URL, node ID and key in place of the placeholders:

    /etc/katana/config.toml
    # katana quick start: serve one Xboard (newV2board) VMess node over
    # WebSocket + TLS. The panel decides the port, the transport and the
    # WebSocket path; this file says which panel and node to serve, and where
    # the certificate is.
    [log]
    level = "info"
    [[node]]
    panel_type = "NewV2board"
    [node.api]
    host = "https://panel.example.com"
    node_id = 1
    key = "replace-with-the-panel-key"
    node_type = "V2ray"
    [node.controller]
    listen_ip = "0.0.0.0"
    update_periodic = 60
    [node.controller.cert]
    mode = "file"
    cert_file = "/etc/katana/cert/fullchain.pem"
    key_file = "/etc/katana/cert/privkey.pem"

    Every key in the file is explained in What each key does below.

  5. Check the config. --test reads the file and builds as much of the node as it can without the panel:

    Terminal window
    katana --test -c /etc/katana/config.toml

    A valid file prints one line on stdout and exits with status 0:

    Configuration OK

    An invalid file prints configuration error: and the reason on stderr, and exits with status 1. A misspelled key, for example, is an error rather than something katana skips:

    configuration error: config parse error: TOML parse error at line 20, column 1
    |
    20 | update_period = 60
    | ^^^^^^^^^^^^^
    unknown field `update_period`, expected one of `listen_ip`, `send_ip`, `update_periodic`, `disable_upload_traffic`, `disable_get_rule`, `disable_sniffing`, `cert`

    Configuration OK does not mean the node will come up. --test makes no panel request, and for a VMess node it checks neither cert.mode nor the certificate files: the example passes even when /etc/katana/cert/fullchain.pem does not exist yet. What –test checks lists exactly what it covers.

  6. Start katana. Run it in the foreground for the first start:

    Terminal window
    sudo katana -c /etc/katana/config.toml

    Binding a port below 1024, such as 443, needs root or the CAP_NET_BIND_SERVICE capability. Without either, the listener fails with Permission denied (os error 13). Once the node works, run katana as a service instead, as described in Deployment.

  7. Read the log. katana writes its log to stdout. When the panel answered and the listener is bound, it logs one line for the node:

    2026-09-24T20:28:49.297288Z INFO katana::manager::node: node 1: listening on 0.0.0.0:443

    node 1 is the node_id from the config, and 0.0.0.0:443 is listen_ip plus the port from the panel. This is the only line katana logs for a node that starts normally. At the default info level it logs nothing per connection. If the line does not appear within a few seconds, see Startup log lines.

  8. Connect a client. Import the panel’s subscription link into a VMess client, or enter the values by hand:

    Client setting Value
    Address proxy.example.com
    Port 443
    User ID the user’s UUID from the panel
    alterId 0
    Security (body cipher) auto, aes-128-gcm or chacha20-poly1305
    Network ws, path /ws
    TLS on, server name proxy.example.com

    katana speaks AEAD VMess only, which is what alterId = 0 selects. It refuses the ciphers none and zero.

  9. Confirm the traffic report. Load a few pages through the client, then wait one update_periodic interval (60 seconds in this config). The user’s upload and download should rise in the panel. Traffic reporting below explains what katana sends and what it logs.

The quick-start config uses eleven keys. katana rejects any key it does not know, in every table, so a typo stops the program instead of being ignored.

Key Value here Meaning
[log].level "info" Log filter, in tracing filter syntax: "info", "debug", "info,katana=debug". The default is "info". The RUST_LOG environment variable, if set, overrides it at startup; a later edit of this key replaces it without a restart.
panel_type "NewV2board" The panel API. "NewV2board" and its alias "V2board" select the UniProxy API that Xboard and V2board serve. "SSpanel" selects mod_mu. Case does not matter. Anything else fails with unknown panel_type.
[node.api].host "https://panel.example.com" The panel’s base URL, with the scheme. katana removes a trailing /.
[node.api].node_id 1 The node ID from the panel. It also names the node in log lines.
[node.api].key "replace-with-the-panel-key" The panel’s communication key. katana sends it as the token query parameter.
[node.api].node_type "V2ray" The node’s protocol family: "V2ray" (aliases "vmess" and "vless"), "Trojan", "Shadowsocks" or "Hysteria2" (aliases "hysteria" and "hy2"). Case does not matter. Anything else fails with unknown node_type. katana sends the value in lowercase as the node_type query parameter, or vless for a V2ray node with enable_vless = true. Whether a V2ray node serves VMess or VLESS is decided by enable_vless, not by this value.
[node.controller].listen_ip "0.0.0.0" The address to bind. The default is "0.0.0.0".
[node.controller].update_periodic 60 Seconds between panel syncs. Each sync fetches the node config and user list, and reports traffic. The default is 60, and 0 counts as 1.
[node.controller.cert].mode "file" Must be "file", in lowercase, for any node with TLS. The default is "none", and a TLS node with any value other than "file" fails when its listener starts. "dns", "http" and "tls" (ACME modes in XrayR) are refused on every node, with or without TLS. For a node that is not Hysteria 2, --test does not check this key.
[node.controller.cert].cert_file path The PEM certificate chain.
[node.controller.cert].key_file path The PEM private key.

Some keys the example leaves out have defaults worth knowing. [node.api].timeout is the limit for each panel request in seconds; 0 or unset means 5. [node.api].enable_vless = true makes a V2ray node serve VLESS instead of VMess. The full list of keys is in Configuration file.

Use absolute paths for cert_file and key_file. katana opens a relative path against its working directory, not against the directory of the config file.

katana --test -c <file> runs the checks that need nothing but the local machine. It binds no port and sends no request to the panel.

Checked by --test Checked only when the node starts
The file exists, is UTF-8 and valid TOML, and contains no unknown key. Whether the panel is reachable, and whether host, node_id and key are right.
There is at least one [[node]]. The port, transport, path and TLS setting. They come from the panel.
Each node’s panel_type and node_type are known values. For every node type except Hysteria 2: cert.mode, and whether cert_file and key_file exist and hold a matching pair.
[dns] and every [[outbound]] build, including the DNS ca_file. listen_ip, and whether the port can be bound.
Each node’s [node.route] compiles: rule syntax, outbound tags, and the geoip and geosite files. Whether the panel has users for the node.
For Hysteria 2 nodes: the [node.hysteria] settings, cert.mode, and the certificate and key files. Features the panel asks for that katana refuses, such as REALITY or an XTLS flow.

The certificate check is different for Hysteria 2 because a Hysteria 2 listener always uses TLS, so katana knows without asking the panel that the node needs a certificate. For every other node type, the panel decides whether TLS is on, so katana checks cert.mode and reads the certificate files only when it starts the listener.

An error that names no file can be misleading. If the config file itself is missing, --test prints only the system error:

configuration error: No such file or directory (os error 2)

When a node does not come up, its log lines say at which stage it stopped, and katana tries again by itself (see Retries after a failed start). Each line from a failed start ends with the wait before the next attempt. The table shows the first failure, which always waits 1 second. The katana::manager::node target and the timestamp are left out below.

Log line Cause Fix
INFO node 1: listening on 0.0.0.0:443 The node is up. None.
ERROR node 1: node_info failed: GET /api/v1/server/UniProxy/config; retrying in 1s The panel request failed: wrong host, the panel is unreachable, or the panel rejected the request (wrong key, unknown node_id, or a node of another type under that ID). Check the three values, then run the request by hand as shown below.
ERROR node 1: node_info failed: parse UniProxy config response; retrying in 1s The panel answered with something that is not a node config, for example an HTML page. Check that host is the panel’s base URL and panel_type matches the panel.
ERROR node 1: node_info failed: newV2board: server port must be > 0; retrying in 1s The node has no service port in the panel. Set the port in the panel.
ERROR node 1: initial start failed: TLS node requires cert.mode = "file"; retrying in 1s TLS is on in the panel, but cert.mode is unset, "none", or any other value than "file" in lowercase. Set mode = "file" and both file paths.
ERROR node 1: initial start failed: node requests kernel-unsupported feature: ACME cert mode "dns"; retrying in 1s cert.mode is "dns", "http" or "tls". katana has no ACME client. Obtain the certificate with another tool and set mode = "file".
ERROR node 1: initial start failed: TLS node requires cert.cert_file and cert.key_file; retrying in 1s mode = "file", but cert_file or key_file is empty or missing. Set both paths.
ERROR node 1: initial start failed: No such file or directory (os error 2); retrying in 1s cert_file or key_file does not exist. The message does not say which. Check both paths.
ERROR node 1: initial start failed: Permission denied (os error 13); retrying in 1s katana may not bind the port, or may not read the key file. Run as root or with CAP_NET_BIND_SERVICE, and check the key file’s permissions.
ERROR node 1: initial start failed: Address already in use (os error 98); retrying in 1s Another program holds the port. Stop it, or change the port in the panel.
ERROR node 1: initial start failed: node requests kernel-unsupported feature: REALITY; retrying in 1s The panel describes a feature katana does not implement. Change the node in the panel. Protocols lists what katana serves.
ERROR node 1: user_list failed: GET /api/v1/server/UniProxy/user; retrying in 1s The user list request failed. katana needs the user list to start a node, so it binds nothing until an attempt gets one. Check the panel, as for node_info failed.
no line at all The panel returned an empty user list, so katana bound nothing. Give at least one user access to the node. katana binds the port at the first sync that returns a user.
ERROR node 1: rebuild failed: No such file or directory (os error 2) The node started with no users, and the listener failed when a later sync brought users. The causes are the same as for initial start failed. Fix the cause. katana tries again at every sync.
WARN node 1: node_info: GET /api/v1/server/UniProxy/config or WARN node 1: user_list: GET /api/v1/server/UniProxy/user A sync after startup failed. The node keeps serving with the node config and users it already has. Check the panel.

The panel error lines never include the URL, because the URL carries the key. To see what the panel answers, send the request katana sends yourself. The key ends up in your shell history, so clear it afterwards:

Terminal window
curl -sS 'https://panel.example.com/api/v1/server/UniProxy/config?node_id=1&node_type=v2ray&token=replace-with-the-panel-key'

A working node answers with JSON that contains server_port, network, networkSettings and tls. katana sends the configured node_type in lowercase, and Xboard reads v2ray as vmess.

A node that fails to start keeps trying until it is up or katana stops. Each attempt requests the node config and the user list afresh and then starts the listener. The first retry comes 1 second after the failure, and the wait doubles after each further failure, up to 60 seconds, or up to update_periodic seconds if that is shorter. With update_periodic = 60, the waits are 1, 2, 4, 8, 16 and 32 seconds, then 60 seconds for every retry after that. The process and any other nodes keep running meanwhile, so a service manager shows katana as running.

Fix the cause, and the node comes up at its next attempt, without a restart:

  • A fix outside katana, such as correcting the node in the panel, freeing the port, or putting the certificate files in place, takes effect at the next retry.
  • Saving an edit to the node’s [[node]] entry in the config file makes katana retry at once instead of waiting out the current wait.
  • A fix to the process itself, such as granting CAP_NET_BIND_SERVICE or changing the systemd unit, needs a restart of katana, because a running process does not pick it up.

katana meters each user’s traffic as it relays and reports it at every sync, that is, every update_periodic seconds. The first sync comes one full interval after the node started. For newV2board panels the report is one request:

POST /api/v1/server/UniProxy/push
{"7":[82,200204]}

Each entry maps a panel user ID to [upload, download] in bytes. katana counts the payload it relays, after decryption, so TLS, WebSocket and VMess overhead is not included. Users who moved no bytes since the last report are left out, and when nobody moved any, katana sends no request.

A successful report logs nothing. A failed one logs a warning:

WARN katana::manager::node: node 1: report traffic: POST UniProxy push

katana keeps the unreported bytes and adds them to the next report, so a failed report delays the figures but does not lose them. When katana stops on SIGINT or SIGTERM, it closes the listener and sends one last report.

If the panel still shows no traffic after two intervals and there is no warning, check that [node.controller].disable_upload_traffic is not true and that the client really goes through this node. Traffic reporting covers the counters in detail.

At the default info level, katana logs nothing for a single failed connection; it logs each one at debug. It warns only when more than 10 handshakes fail within one second on a node. That warning names the node by its tag, the node type, listen_ip and port:

WARN katana::manager::proxy: node V2ray_0.0.0.0_443: 12 inbound handshake failures in the last 1s (possible handshake scan/DoS or misconfigured clients)

To see why one client fails, set [log].level = "info,katana=debug" for a while (katana applies a new level without a restart), or check the client against the node:

  • WebSocket path. The path must match the panel’s path exactly. katana answers any other path with HTTP 404.
  • WebSocket host. If the node sets a Host header in the panel, the client’s Host must match it, ignoring case and port. katana answers a mismatch with HTTP 404 as well.
  • TLS server name. The certificate must cover the name the client sends.
  • User. The UUID must belong to a user the panel currently sends to the node. A user added in the panel reaches katana at the next sync.
  • Clock. VMess authentication fails when the client’s clock is more than 120 seconds away from the server’s.

Press Ctrl-C, or send SIGTERM. katana logs shutting down, closes the listeners, reports any traffic not yet reported, and exits with status 0.

While katana runs, it watches the config file’s directory and applies edits without a restart. This includes [node.api]: an edit that points the node at a different panel node, such as a new node_id, restarts that node, and any other [node.api] edit takes effect at once. Hot reload explains which edits take effect and which drop connections. A renewed certificate is not picked up this way: katana reads the certificate files when it starts a listener, so restart katana after each renewal.