Errors and step-by-step checks
Start with safe commands that do not change the system, identify the failing stage, and only then edit configuration.
Five commands before changing config
They do not create TUN devices or change routes; they only read system state. Save the exact first error because later messages are often consequences of it.
$ qeli --version
$ sudo qeli check-config --config /etc/qeli/server.conf
$ sudo systemctl status qeli --no-pager
$ sudo journalctl -u qeli -n 200 --no-pager
$ sudo ss -lntup | grep qeli
Symptom → likely area
| Symptom | Check first | Next step |
|---|---|---|
| The service keeps restarting | check-config, journal, TUN/bind collision | Server startup ↓ |
| The client times out | Port, firewall, tcp/udp transport and public address | Handshake ↓ |
| Connected, but AUTH is rejected | user/pass, profiles, enabled, lockout | Handshake ↓ |
| Tunnel is up, but there is no internet | gateway, NAT, ip_forward, firewall | Network ↓ |
| Small pages work, but downloads stall | MTU, MSS clamp, PMTU black-hole | Network ↓ |
| An IP opens, but a domain does not | dns.enabled, dns.push_servers, client DNS mode | Network ↓ |
| The panel returns 403 | allowed_ips or allowed_origins | Panel ↓ |
| The panel behind a proxy returns 404 or redirects to root | base_path, trusted proxy, prefix stripping | Panel ↓ |
| Reconnect after sleep takes too long | Client version, network switch and battery policy | Clients ↓ |
The service does not start a working profile
The 0.8.1 validator finds syntax errors, unknown keys, user errors, overlapping IPv4/IPv6 pools, duplicate TUN names, bind collisions, unreachable REALITY targets and incompatible roaming/recordizer policies.
sudo qeli check-config --config /etc/qeli/server.confvalidate before restartsudo journalctl -u qeli -b --no-pagerjournal for the current bootsudo ip tuntap showexisting TUN/TAP devices and name collisionssudo ss -lntupwhich process already owns the portgetent passwd qeliwhether the service user existsAfter manual edits, restore file ownership: the service and panel must read and atomically update /etc/qeli. For a .deb installation the usual owner is qeli:qeli.
From listener to AUTH
Split the failure by stage: no TCP/UDP connection, wire-mode mismatch, pinning/REALITY failure, or user rejection by the server.
| Stage | What to verify |
|---|---|
| Connect | Public host:port, TCP/UDP, firewall and port forwarding |
| Wire mode | proto, mode, obfs_key, QUIC toggle |
| REALITY / H-1 | key, reality_sid, sni, target reachability |
| AUTH | user, pass, enabled, profiles, expire_at, quota |
| Lockout | sudo qeli list-blocked / sudo qeli unblock <ip> |
$ sudo qeli show-identity --config /etc/qeli/server.conf
# the client key must match the profile it connects to
Routes, NAT, MTU and DNS
If the handshake completed and TUN is up, look in the routing table, forwarding, NAT/firewall, MTU or resolver.
$ sudo qeli show-routes <user>
$ ip route show
$ sudo sysctl net.ipv4.ip_forward
$ sudo iptables-save | grep -iE 'qeli-nat|MASQUERADE|TCPMSS'
$ ping -c 3 10.9.0.1
$ getent hosts example.com
| Observation | Meaning |
|---|---|
| No route to the TUN pool | The client did not finish interface setup or the route conflicts |
| tun.address responds, but internet does not | Check gateway=true, NAT, ip_forward and FORWARD |
| Small responses pass, large ones stall | Typical PMTU black hole: MSS clamp or MTU is too large |
| IP works, DNS name does not | Compare dns.push_servers, dns.listen and the local dns mode |
| Push appears in the log, but the route is absent | Platform application, permissions or an OS routing-table conflict |
Panel: 403, 404 and no access
| Symptom | What to fix |
|---|---|
| VPN works, but :8080 is not listening | Set password_hash with set-web-password; the panel fails closed even on loopback |
| 403 when opening the page | Your source IP is not in allowed_ips |
| The page opens, but Save returns 403 | Add the browser host[:port] to allowed_origins |
| Behind a proxy you get 404 or a redirect to / | Align base_path with X-Forwarded-Prefix; do not strip the prefix when base_path is set |
| Apply & Restart does not work | sudo qeli install-polkit |
| QR cannot be generated for an existing user | Check the users.conf owner and that /var/lib/qeli/panel-secret.key exists |
Client-specific cases
| Platform | Typical case | Action |
|---|---|---|
| Android | protect() failed | Disable a conflicting always-on VPN or Block connections without VPN |
| Android | Slow recovery after sleep | Use client 0.8.1, keep roaming=auto, and check the vendor's battery restrictions |
| Windows / macOS | Pushed gateway/metric was not applied | The CIDR is still bound to TUN; read the explanatory log line |
| macOS | Tunnel DNS remains after removal | Run the client's normal disconnect/cleanup and inspect system DNS |
| Windows/macOS on the same LAN as the server | Reconnect loop in full-tunnel mode | Set local to the client's LAN IP so the carrier reaches the server on-link without an asymmetric /32 through the gateway |
Debug log and QELI_TRACE
Enable debug only while reproducing the issue. QELI_TRACE records packet shape and timing without payloads or addresses; still inspect the file before publishing it.
$ sudo systemctl edit qeli
# add:
[Service]
Environment=RUST_LOG=debug
Environment=QELI_TRACE=/tmp/qeli-server.csv
$ sudo systemctl daemon-reload
$ sudo systemctl restart qeli
Complete message and error catalogue
The repository covers connection stages, exact log messages, UDP/REALITY errors and additional platform scenarios.