Skip to main content

Configuration file

b4 keeps its configuration in a single JSON file. The default is /etc/b4/b4.json, and --config points it somewhere else.

Where it lives​

PlatformPath
Linux/etc/b4/b4.json
OpenWRT (with extroot or USB storage)/opt/etc/b4/b4.json
OpenWRT (without USB storage)/etc/b4/b4.json
ASUS Merlin/opt/etc/b4/b4.json
Keenetic/opt/etc/b4/b4.json
Docker/etc/b4/b4.json, inside the container

Without --config, b4 looks for b4.json and config.json under /etc/b4 and /opt/etc/b4, and falls back to /etc/b4/b4.json. The path it settled on is written to the log at startup.

Downloading the file

The Download Configuration card under Settings, System, Backup saves the configuration in the form b4 writes to this file. The safe copy masks credentials and private host names for sharing, and b4 refuses to load or save a configuration that still holds its [redacted] placeholders.

Only what differs from the defaults is stored​

The file is sparse. A setting that still holds its built-in default is left out of it entirely, so a fresh installation produces a very short file, and a section whose settings were never changed is absent. That is not a sign of a missing setting, and adding it back by hand with its default value changes nothing.

The same applies to saves through the API and the web interface: the file then holds what differs from the defaults, not the full effective configuration.

Structure​

{
"version": 52,
"queue": {
"start_num": 537,
"threads": 4,
"mark": 32768,
"ipv4": true,
"ipv6": false,
"tcp_conn_bytes_limit": 19,
"udp_conn_bytes_limit": 8,
"interfaces": [],
"mss_clamp": { "enabled": false, "size": 88 },
"devices": { "enabled": false, "vendor_lookup": false }
},
"system": {
"tables": {
"skip_setup": false,
"monitor_interval": 10,
"engine": "",
"masquerade": { "enabled": false, "interfaces": [] },
"dscp": { "enabled": false, "value": 0, "interfaces": [] }
},
"logging": {
"level": 1,
"directory": "/var/log/b4",
"instaflush": true,
"syslog": false
},
"web_server": {
"port": 7000,
"bind_address": "0.0.0.0",
"expose": false,
"tls_cert": "",
"tls_key": "",
"username": "",
"password": "",
"language": "en",
"mcp": { "enabled": false, "allow_writes": false }
},
"dns": {
"tcp_disabled": false,
"tcp_port": 5453,
"query_timeout_sec": 5,
"keep_ipv6_answers": false
},
"socks5": {
"enabled": false,
"port": 1080,
"bind_address": "0.0.0.0",
"expose": false,
"allowed_sources": ["192.168.1.0/24", "127.0.0.1/32"]
},
"mtproto": { "enabled": false, "port": 3128, "bind_address": "0.0.0.0", "expose": false },
"checker": {
"discovery_timeout": 5,
"config_propagate_ms": 1500,
"dns_server": ""
},
"geo": { "sitedat_path": "", "ipdat_path": "", "sitedat_url": "", "ipdat_url": "" },
"timezone": ""
},
"sets": []
}

The sample shows only some of the sections. Every section holds more keys than are shown, and none of them appear in a real file until they differ from the default.

mtproto holds more than the three keys above. secrets is an array of named entries, each with id, name, secret and enabled; a file written before configuration version 50 carried a single mtproto.secret string, which the migration moves into that array as an entry named default. web_proxy is an object with enabled and hostname, and both of its fields are zero-valued by default, so a working relay is the only reason they appear in a file at all. bridge holds a single enabled field, the Telegram over WebSocket switch, and appears only while it is on. See Settings, Telegram.

socks5.allowed_sources is one of those keys: it is absent while the list is empty, which is the default and means the proxy accepts a connection from any source. Each entry is an IP address or a CIDR range, and 0.0.0.0/0, ::/0 and malformed entries are refused when the file is loaded or saved. It gates which addresses reach the proxy, the way a firewall rule does, and is not a substitute for the username and password. See Allowed sources.

The queue section​

KeyMeaningDefault
start_numNetfilter queue number the workers bind to537
threadsNumber of worker threads4
markQueue mark: the packet mark on the fakes, split segments and packets b4 sends back out, and on the DNS queries it sends for clients and for its sets, see Packet marks32768
ipv4Process IPv4 traffictrue
ipv6Process IPv6 trafficfalse
tcp_conn_bytes_limitGlobal ceiling on how many TCP packets per connection are analysed19
udp_conn_bytes_limitGlobal ceiling on how many UDP packets per connection are analysed8
interfacesThe Capture Interfaces field, a filter on the interface a packet passes, see Capture Interfaces. Empty means all of them[]

queue.ipv6​

queue.ipv6 is the Enable IPv6 Support switch from Settings, Core, Packet Engine, and --ipv6 on the command line sets the same thing for one run. It says which address families b4 processes, not what the router does with IPv6.

Off, which is the default, means b4 binds its queue to IPv4 only and writes no IPv6 firewall rules for any set. Bypass strategies, routing, blocking and a proxy set's QUIC refusal then exist on IPv4 alone, so a destination that also answers over IPv6 is reachable there with nothing in the way. b4 logs a warning when the host has a working global IPv6 address while this is off, and it strips IPv6 addresses out of DNS answers for matched domains to keep clients on the protected IPv4 path. See The IPv4 fallback.

The address families are bound when the service starts, so a change here needs a restart to take full effect.

The sets section​

Each set is one object in the sets array, carrying its whole configuration. Its keys line up with the tabs of the set editor:

  • targets - domains, IPs, GeoSite and GeoIP categories, ASNs, source devices
  • tcp - general TCP settings, desync, window, incoming, RST protection
  • fragmentation - the fragmentation method and its parameters
  • faking - SNI faking, SYN fakes, mutation
  • udp - the QUIC filter, the port filter and the UDP action mode
  • dns - the set's resolver, DoH URL and pins
  • routing - routing mode, output interface, upstream proxy or blocking
  • escalate - which set to escalate to when this one stops working
Import and export

The set editor has an Import/Export tab for moving a set between devices. It shows the set as JSON and takes a pasted one back. The exported JSON leaves out values equal to the defaults, the settings of switched-off features, the set's id, its source devices, its watchdog switch and its escalation settings.

Editing it by hand​

version at the top is the configuration format version. b4 reads it to decide what needs migrating, so it should stay unchanged when anything else is edited.

warning

Editing by hand works, but the values in the file are checked only when b4 starts, and a file that fails the check stops b4 with an invalid configuration error. The web interface refuses an invalid value when it is saved. b4 reads the file only at start, so a manual edit takes effect after a restart. A save made before then, in the web interface, over MCP, by a watchdog heal, by a geodata download or by an ASN prefix update, writes the running configuration over the file and the manual edit is lost.

Migrations​

When the format changes between releases, b4 migrates the file on startup: new fields arrive with their defaults and renamed ones are carried over. Before it touches anything it writes a backup next to the file, named after the version it is migrating from, for example b4.json.v51.bak.

A file that cannot be read at startup, for example one cut short by a power loss during a save or left with a JSON syntax error after a manual edit, is copied next to itself as b4.json.corrupt, or b4.json.corrupt.1 and onward when an earlier copy with different content is already there, and an error line in the log names the copy. b4 then starts on the defaults, and the next save replaces the original file, so the unreadable settings remain only in that copy.

A file whose version is higher than the running binary understands is loaded as it is, with a warning: settings that binary does not know about are dropped the next time the configuration is saved.