# Pebble configuration reference (schema version 1). # # Every setting Pebble understands appears below with a comment. This file # is a valid configuration: 'pebble validate -c annotated.yaml' accepts it. # Unknown keys are rejected, so a typo is an error, never silently ignored. # # Durations accept s, m, h and d suffixes (90s, 30m, 12h, 7d). version: 1 # required; only 1 is supported system: hostname: pebble # single label; also published as . timezone: Europe/London # a name under /usr/share/zoneinfo ntp: true # default true; enables ntpd with sync-on-start (DNSSEC needs a correct clock) network: wan: device: igb0 # never guessed: see 'pebble interfaces' mtu: 1500 # optional, 576..9216 ipv4: mode: dhcp # dhcp | static # address: 203.0.113.10/29 # static only # gateway: 203.0.113.9 # static only, inside the WAN prefix ignore_dns: true # default: true when dns.enabled; the ISP's DNS servers are not used by the router itself lan: device: igb1 ipv4: address: 192.168.10.1/24 # the router's address and the LAN prefix (/8../30) ipv6: mode: disabled # only "disabled" in version 1: all IPv6 is dropped by PF and never forwarded routes: # static IPv4 routes; the default route always belongs to the WAN - destination: 10.50.0.0/16 gateway: 192.168.10.254 # must be inside a directly connected network description: Lab behind an internal router # - destination: 172.31.0.0/16 # interface: wg0 # alternatively, route into a WireGuard interface dhcp: enabled: true authoritative: true # answer for the whole LAN (recommended when Pebble is the only DHCP server) start: 192.168.10.100 # the range may not contain the router's address end: 192.168.10.199 lease_duration: 12h # 2m..30d domain: home.arpa # local domain handed to clients and served by Unbound # dns_servers: [192.168.10.1] # default: the router's LAN address when dns.enabled reservations: - name: nas # identifier (lower case) mac: "02:00:5e:00:53:01" address: 192.168.10.20 hostname: nas # optional; becomes nas.home.arpa in DNS register_dns: true # default true # egress: wg1 # Internet via: absent = WAN, a VPN exit (role: egress), or none = no Internet options: # typed subset: ntp-server, domain-search, mtu, tftp-server, bootfile-name - name: ntp-server value: [192.168.10.1] dns: enabled: true recursive: true # resolve from the root servers; false requires forwarders # forwarders: [9.9.9.9, 149.112.112.112] dnssec: true cache_mb: 32 # total Unbound cache, 4..1024 MB listen_wireguard: false # also answer on WireGuard tunnel addresses (for remote sites) allow_from: [] # extra client networks behind LAN routers or WireGuard (never WAN-only networks) log_queries: false # default false: individual queries are not logged query_log: # per-device lookup history on the Devices page (needs monitoring.enabled) enabled: false # default false: no lookups are kept retention: 24h # 1h to 7d; older lookups are deleted max_mb: 64 # size cap for the whole history records: - {name: printer.home.arpa, type: A, value: 192.168.10.30} - {name: www.home.arpa, type: CNAME, value: nas.home.arpa} - {name: home.arpa, type: TXT, value: "pebble router"} local_zones: - {name: ads.internal.example, type: always_nxdomain} # static | transparent | refuse | always_nxdomain forwards: # conditional forwarding, for example to another site over WireGuard - domain: corp.example servers: [10.50.0.53] # address or address@port insecure: true # skip DNSSEC validation for this (unsigned, private) domain blocking: enabled: true response: nxdomain # nxdomain | null (0.0.0.0) update_interval: 24h # 1h 2h 3h 4h 6h 8h 12h 24h or 7d, scheduled with cron max_domains: 250000 # hard limit on the compiled list; exceeding it keeps the previous list max_download_mb: 32 # per feed, after decompression lists: - name: stevenblack # [a-z0-9_-] url: https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts # HTTPS only format: hosts # auto | domains | hosts allowlist: [s.youtube.com] # never blocked, including when a parent domain is listed denylist: [telemetry.example.com] # always blocked, with all subdomains wireguard: interfaces: [] # see examples/site-to-site.yaml, and examples/vpn-egress.yaml for a VPN provider exit firewall: default_inbound: deny # only "deny": unsolicited traffic to the router is dropped default_forward: deny # deny | allow (allow = forward freely between LAN and WireGuard networks; never from the WAN) default_outbound: allow # only "allow" in version 1 allow_lan_to_wan: true # default true: LAN may reach the Internet (internal/site destinations need rules) log_blocked: false # log dropped packets to pflog0 max_states_per_device: 20000 # default 20000: connections one LAN address may hold (0 = no cap) lan_internet: all # all (default) | known: only the DHCP pool and reservations reach the Internet icmp: lan_echo: true # default true: LAN may ping the router wan_echo: false # default false nat: enabled: true outbound: wan # only "wan" # sources: [192.168.10.0/24] # default: the LAN prefix rules: # evaluated in order on arrival; first match wins - description: Printers may not reach the Internet action: block # pass | block interface: lan # lan | wan | protocol: any # any | tcp | udp | tcp/udp | icmp source: 192.168.10.30 # any | self | lan | peer: | address | prefix destination: any log: true - description: Drop traffic from addresses in a downloaded list action: block interface: wan source: list:abusers destination: any ip_lists: # downloaded IPv4 lists, loaded into PF tables; use as list: in rules update_interval: 24h # 1h 2h 3h 4h 6h 8h 12h 24h or 7d, scheduled with cron max_download_mb: 8 # per list, after decompression lists: - name: abusers # [a-z][a-z0-9_]{0,21} url: https://www.spamhaus.org/drop/drop.txt # HTTPS only; one address or prefix per line, '#'/';' comments max_entries: 50000 # a larger download is rejected and the previous list kept port_forwards: - description: Web server on the NAS protocol: tcp # tcp | udp | tcp/udp external_port: "8443" # port or range on the WAN address target: 192.168.10.20 target_port: "443" # default: same as external_port sources: [198.51.100.0/24] # default: anywhere management: ssh: enabled: true port: 22 allow_from: [192.168.10.0/24] # evaluated before your rules (anti-lockout) allow_wan: false # must be true before any WAN-only network may be listed updates: automatic_install: false # only false: Pebble never installs updates by itself recovery: rollback_timeout: 120s # 30s..1h: time to run 'pebble confirm' after a disruptive change keep_generations: 5 # previous deployments kept for 'pebble rollback'