Skip to content

Configuration

mqvpn supports both INI and JSON config files. If the file content starts with {, it is parsed as JSON; otherwise as INI. CLI arguments override config values.

INI Format

Server

ini
# /etc/mqvpn/server.conf
[Interface]
Listen = 0.0.0.0:443
Subnet = 10.0.0.0/24
Subnet6 = 2001:db8:1::/112
# MTU = 1280

[TLS]
Cert = /etc/mqvpn/server.crt
Key = /etc/mqvpn/server.key

[Auth]
Key = mPyVpoQWcp/5gr404xvS19aRC03o0XS2mrb2tZJ1Ii4=
User = alice:<ALICE_PSK>
User = bob:<BOB_PSK>

[Multipath]
Scheduler = wlb
# CC = bbr2                     # Congestion control (bbr2|bbr|cubic|none)

Client

ini
# /etc/mqvpn/client.conf
[Server]
Address = 203.0.113.1:443

[Auth]
Key = mPyVpoQWcp/5gr404xvS19aRC03o0XS2mrb2tZJ1Ii4=

[Interface]
TunName = mqvpn0
DNS = 1.1.1.1, 8.8.8.8
LogLevel = info
# MTU = 1280

[Multipath]
Scheduler = wlb
# CC = bbr2                     # Congestion control (bbr2|bbr|cubic|none)
Path = eth0
Path = wlan0

JSON Format

JSON config is useful for structured management and automation tooling.

Server

json
{
  "mode": "server",
  "listen": "0.0.0.0:443",
  "tun_name": "mqvpn0",
  "log_level": "info",
  "subnet": "10.0.0.0/24",
  "subnet6": "2001:db8:1::/112",
  "cert_file": "/etc/mqvpn/server.crt",
  "key_file": "/etc/mqvpn/server.key",
  "auth_key": "<YOUR_PSK_HERE>",
  "users": [
    { "name": "alice", "key": "<ALICE_PSK>" },
    { "name": "bob", "key": "<BOB_PSK>" }
  ],
  "max_clients": 64,
  "scheduler": "wlb",
  "cc": "bbr2",
  "mtu": 1280
}

Client

json
{
  "mode": "client",
  "server_addr": "203.0.113.1:443",
  "tun_name": "mqvpn0",
  "log_level": "info",
  "auth_key": "<YOUR_PSK_HERE>",
  "insecure": false,
  "dns": ["1.1.1.1", "8.8.8.8"],
  "kill_switch": false,
  "reconnect": true,
  "reconnect_interval": 5,
  "scheduler": "wlb",
  "cc": "bbr2",
  "paths": ["eth0", "wlan0"],
  "mtu": 1280
}

Multi-User Authentication

The server can authenticate multiple users, each with their own PSK. In JSON config, add a users array where each entry is either an object ({"name":"alice","key":"..."}) or a shorthand string ("alice:key"). In INI config, use repeatable User = NAME:KEY lines in the [Auth] section. You can also use the Control API to manage users at runtime.

When both auth_key (global key) and users are set, clients can authenticate with either. To restrict access to named users only, remove auth_key from the config.

Removing a user via the Control API also disconnects any active sessions authenticated with that username.

Monitoring requires per-user keys

Sharing a single auth_key across multiple clients works for the VPN data plane, but the Control API and the Prometheus exporter identify clients by their user label. Sessions authenticated with the global auth_key are reported as user="(global)", so multiple clients collide on the same label and the Prometheus scrape is dropped. For multi-client monitoring give each client its own entry under users (or register them at runtime via add_user).

Running with Config Files

bash
sudo mqvpn --config /etc/mqvpn/server.conf
sudo mqvpn --config /etc/mqvpn/server.json

Config Reference

[Server] (client only)

KeyDescriptionDefault
AddressServer address (HOST:PORT, e.g. [2001:db8::1]:443 for IPv6)Required
InsecureSkip TLS certificate verificationfalse

[Interface]

KeyDescriptionDefault
ListenListen address (HOST:PORT, server only)0.0.0.0:443
SubnetClient IPv4 pool (server only)10.0.0.0/24
Subnet6Client IPv6 pool (server only)
TunNameTUN device namemqvpn0
DNSDNS servers (comma-separated)
LogLevelLog level (debug, info, warn, error)info
KillSwitchBlock traffic outside the VPN tunnel (client only)false
ReconnectEnable automatic reconnection (client only)true
ReconnectIntervalSeconds between reconnection attempts5
MTUTUN device MTU cap (1280–9000). If the negotiated MTU is lower, the negotiated value is used.auto

[TLS] (server only)

KeyDescriptionDefault
CertTLS certificate path (PEM)Required
KeyTLS private key path (PEM)Required

[Auth]

KeyDescriptionDefault
KeyPre-shared key (base64, generate with mqvpn --genkey)Required unless User is set
UserPer-user PSK in NAME:KEY format (repeatable)
MaxClientsMaximum concurrent clients (server only)64

[Multipath]

KeyDescriptionDefault
SchedulerScheduler algorithm (minrtt, wlb, wlb_udp_pin, or backup_fec)wlb
CCCongestion control algorithm (bbr2, bbr, cubic, or none)bbr2
PathNetwork interface to bind (repeatable)Default interface

See Multipath for scheduler details.

backup_fec is experimental and requires both peers to run mqvpn ≥ 0.4.0 with FEC build enabled (-DXQC_ENABLE_FEC=ON -DXQC_ENABLE_XOR=ON). See Multipath.

CC = none (no congestion control) requires xquic built with -DXQC_ENABLE_UNLIMITED=ON.

MTU Guidelines

Default (auto) — most deployments

For most setups, leave MTU unset. The auto-negotiated value (~1382) works on standard Ethernet (1500), PPPoE (1492), and mobile networks.

When to set MTU explicitly

ScenarioRecommendation
Standard Ethernet / mobileLeave unset (auto ~1382)
Symmetric client↔server MTUSet MTU = 1280 on both sides
Deeply nested tunnels (mqvpn → WG → another tunnel)Calculate remaining MTU; set if near 1280

If MTU is set in the config, mqvpn uses min(config MTU, negotiated MTU). A warning is logged when the config value exceeds the negotiated value.

TIP

Setting MTU above the negotiated value (~1382) has no effect — the negotiated value is always used as the upper bound.

How mqvpn determines TUN MTU

mqvpn negotiates the TUN MTU from the QUIC DATAGRAM Maximum Segment Size (MSS) at connection time. With the default max_pkt_out_size of 1400, the overhead breakdown is:

max_pkt_out_size           1400 bytes
 − QUIC short header         13 bytes
 − DATAGRAM frame header      3 bytes
 − MASQUE datagram header      2 bytes
                           ─────────
 = TUN MTU                  1382 bytes

Running other tunnels inside mqvpn

When running a tunnel protocol (WireGuard, IPsec, GRE, etc.) inside the mqvpn tunnel, the inner tunnel's overhead reduces the effective MTU. Verify that the remaining MTU meets the inner protocol's requirements.

Example: WireGuard inside mqvpn

mqvpn TUN MTU                    1382 bytes
 − WireGuard overhead (IPv6)        80 bytes
                                 ─────────
 = WireGuard inner MTU            1302 bytes
   → IPv6 minimum (1280)            ✓
   → QUIC/HTTP3 UDP payload        1254 bytes > 1200  ✓

Constraints

ConstraintValueSource
Config minimum1280IPv6 minimum MTU (RFC 8200)
Config maximum9000Jumbo frame MTU
QUIC minimum UDP payload1200RFC 9000 §14 (handshake requirement)
Negotiated upper bound~1382Derived from max_pkt_out_size (1400)

Control API

A running server can be managed at runtime over a local TCP socket using JSON commands. This is useful for adding or removing users without restarting the server.

Enable

bash
sudo mqvpn --mode server ... --control-port 9090

The control API binds to 127.0.0.1 by default. It has no authentication, so only bind to trusted interfaces.

Enable from config file

The control API can also be enabled from /etc/mqvpn/server.conf:

ini
[Control]
Listen = 127.0.0.1:9090

…or from the JSON equivalent:

json
{
  "control_listen": "127.0.0.1:9090"
}

CLI flags (--control-port, --control-addr) override the config-file values per field. --control-port 0 explicitly disables the API even if [Control] Listen is set in the config.

Commands

Add a user:

bash
echo '{"cmd":"add_user","name":"carol","key":"carol-secret"}' | nc 127.0.0.1 9090

Remove a user:

bash
echo '{"cmd":"remove_user","name":"carol"}' | nc 127.0.0.1 9090

Removing a user also disconnects any active sessions authenticated with that username.

List users:

bash
echo '{"cmd":"list_users"}' | nc 127.0.0.1 9090

Get stats:

bash
echo '{"cmd":"get_stats"}' | nc 127.0.0.1 9090

Get detailed status (per-client, per-path):

bash
echo '{"cmd":"get_status"}' | nc 127.0.0.1 9090

Or use the built-in status command for human-readable output:

bash
mqvpn --status --control-port 9090

All commands return a JSON response with an "ok" field. Each connection handles one command, then the server closes the connection.

systemd

If you installed via the deb package or install.sh, the systemd units are already in place. For source builds, install manually:

bash
sudo cmake --install build --prefix /usr/local

Server

If you used install.sh, /etc/mqvpn/server.conf is already generated. To configure manually, copy the example:

bash
sudo cp /etc/mqvpn/server.conf.example /etc/mqvpn/server.conf
sudo vi /etc/mqvpn/server.conf   # edit cert paths, auth key, etc.
sudo systemctl enable --now mqvpn-server

Client (template unit)

The client uses a template unit — the instance name maps to the config file:

bash
sudo cp /etc/mqvpn/client.conf.example /etc/mqvpn/client-home.conf
sudo vi /etc/mqvpn/client-home.conf   # edit server address, auth key, etc.
sudo systemctl enable --now mqvpn-client@home
# → reads /etc/mqvpn/client-home.conf

INFO

The systemd units expect INI .conf files. The server unit's NAT helper scripts also parse the INI config directly, so JSON cannot be used with the standard units as-is.

Released under the Apache License 2.0