Installing Debian 11 on a Protectli VP2410 (Part 1)

I've replaced the hydrus server, a PC Engines APU1D4, with a Protectli VP2410. Debian is the installed OS, as I was unable to find a wireless card with the required form factor that supported HOSTAP mode in FreeBSD. The install was therefore a learning experience.


The VP2410 base:

The package includes a Universal Power Supply, VESA mount kit, Serial Console Cable, SATA data and power cables for internal SSD, Quick Start Guide

From Protectli, I also added 16GB memory and a wifi card (Protectli M.2 802.11ac WiFi Kit). The card supports IEEE 802.11ac/a/b/g/n (2T2R) Bluetooth v5.0, v4.2, v4.1, v4.0 LE, v3.0+HS, v2.1+EDR and is based on the Qualcomm QCA6174A-5 chipset.

The hard disk I bought separately; a Crucial BX500 1TB 3D NAND SATA 2.5 Inch Internal SSD. Fitting the drive was a little more difficult than I expected, since levering off the base plate (after the four holding screws had been removed) required some careful effort.

Once assembled, I installed Debian 11 off a USB drive using the following partition scheme:

  Filesystem      Size  Used Avail Use% Mounted on
  /dev/sda6        44G  8.1G   34G  20% /
  /dev/sda2        46G  2.9G   41G   7% /var
  /dev/sda3       9.1G  100K  8.6G   1% /tmp
  /dev/sda4        46G  5.8G   38G  14% /home
  /dev/sda5       229G   88G  130G  41% /rep
  /dev/sda1       1.9G  5.8M  1.9G   1% /boot/efi

About half of the disk is left unallocated. I'll think of something to do with it later.

The wifi card was recognised, but the firmware failed to load:

  root@ash:~/Desktop# dmesg|grep ath
  [ 4.961046] ath10k_pci 0000:08:00.0: enabling device (0000 -> 0002)
  [ 4.963358] ath10k_pci 0000:08:00.0: pci irq msi oper_irq_mode 2 irq_mode 0 reset_mode 0
  [ 5.255443] usbcore: registered new interface driver ath3k
  [ 5.271062] ath10k_pci 0000:08:00.0: firmware: failed to load ath10k/pre-cal-pci-0000:08:00.0.bin (-2)
  [ 5.271087] ath10k_pci 0000:08:00.0: firmware: failed to load ath10k/cal-pci-0000:08:00.0.bin (-2)
  [ 5.273616] ath10k_pci 0000:08:00.0: firmware: direct-loading firmware ath10k/QCA6174/hw3.0/firmware-6.bin
  [ 5.276890] ath10k_pci 0000:08:00.0: qca6174 hw3.0 target 0x05020000 chip_id 0x003409ff sub 168c:3361
  [ 5.276894] ath10k_pci 0000:08:00.0: kconfig debug 0 debugfs 0 tracing 0 dfs 0 testmode 0
  [ 5.280497] ath10k_pci 0000:08:00.0: firmware ver WLAN.RM.4.4.1-00157-QCARMSWPZ-1 api 6 features wowlan,ignore-otp,mfp crc32 90eebefb
  [ 5.346846] ath10k_pci 0000:08:00.0: firmware: direct-loading firmware ath10k/QCA6174/hw3.0/board-2.bin
  [ 5.351031] ath10k_pci 0000:08:00.0: board_file api 2 bmi_id N/A crc32 318825bf
  [ 12.618777] ath10k_pci 0000:08:00.0: wmi unified ready event not received
  [ 12.668878] ath10k_pci 0000:08:00.0: device has crashed during init
  [ 12.696823] ath10k_pci 0000:08:00.0: device has crashed during init
  [ 12.696831] ath10k_pci 0000:08:00.0: failed to wait for target init: -70
  [ 12.697882] ath10k_pci 0000:08:00.0: could not init core (-110)
  [ 12.697957] ath10k_pci 0000:08:00.0: could not probe fw (-110)

Protectli can provide some modified (downgraded?) firmware to replace a couple of the modules installed by the atheros-firmware package.

  sudo cp wifi-fix-standalone/vendor/{board-2.bin,firmware-6.bin} \

On reboot, the wireless card firmware was successfully loaded.

snd_hda_intel errors

These errors appeared on the console:

  snd_hda_intel: azx_get_response timeout, switching to polling mode:
    last cmd=0x12345678
  snd_hda_intel: azx_get_response timeout, switching to single_cmd mode:
    last cmd=0x12345678

The workaround (which seems to work) is to add an option to the Linux kernel boot, in /etc/default/grub:


Setting up the wireless/wired bridge for access point

Install hostapd, bridge-utils, isc-dhcp-server.

Creating the bridge

The bridge is setup in /etc/network/interfaces:

  # This file describes the network interfaces available on your system
  # and how to activate them. For more information, see interfaces(5).

  source /etc/network/interfaces.d/*

  # The loopback network interface
  auto lo br0
  iface lo inet loopback

  # Set up interfaces manually, avoiding conflicts with, e.g., network
  # manager
  iface en01 inet manual
  iface enp2s0 inet manual
  iface enp3s0 inet manual
  iface enp6s0 inet manual
  iface wlp8s0 inet manual

  iface br0 inet static
    bridge_ports eno1 enp2s0 enp3s0 enp6s0 wlp8s0

The wireless part of the bridge won't work properly until hostapd is configured.

Configuring hostapd

Configuration of hostapd is set in the file /etc/hostapd/hostapd.conf.

  # this next is key; tells hostapd that the wifi interface is part of a
  # bridge (This has to be done via hostapd because wlps80 only becomes
  # bridgeable when it's actually switched to "access point" mode.)
  rsn_pairwise=CCMP #TKIP ok as well?

My reference for this is this post.

DHCP server

The initial configuration of isc-dhcp-server is reasonably straightforward:

  # option definitions common to all supported networks...
  option domain-name "";
  option domain-name-servers,;

  default-lease-time 7200;
  max-lease-time 28800;

  ddns-update-style none;

  # If this DHCP server is the official DHCP server for the local
  # network, the authoritative directive should be uncommented.

  shared-network "" {
      subnet netmask {
          interface br0;
          option routers;
          option subnet-mask;
          option broadcast-address;

To prevent the dhcpv6 version running, update the variable INTERFACESv4 in /etc/default/isc-dhcp-server:


The configuration of dhcpd gets more interesting when we setup dynamic DNS in BIND9.

BIND9 name server

I could copy across the basic configuration files for BIND from my FreeBSD box, so I won't document them here. The ad blacklist file, downloaded from Peter Lowe's site had to be edited to point the zone files to /etc/bin/db.blacklist.

To enable dynamic DHCP, I had to setup an rdnc key, shared with dhcpd. The method I used in FreeBSD, that is setting the IP addresses of valid updaters, I could not get to work in Debian.

rdnc-confgen will generate configuration stanzas, which can then be included in the appropriate configuration files. Sample output looks like this:

  # Start of rndc.conf
  key "rndc-key" {
          algorithm hmac-sha256;
          secret "random string";

  options {
          default-key "rndc-key";
          default-port 953;
  # End of rndc.conf

  # Use with the following in named.conf, adjusting the allow list as needed:
  # key "rndc-key" {
  #       algorithm hmac-sha256;
  #       secret "random string";
  # };
  # controls {
  #       inet port 953
  #               allow {; } keys { "rndc-key"; };
  # };
# End of named.conf

The first section was placed in /etc/bind/rdnc.conf. This is for the benefit of rndc. The second section, sans comment characters, was copied into /etc/bind/rndc-keys/rndc.key and included at the head of /etc/bind/named.conf.local file with:

  include "/etc/bind/rndc-keys/rndc.key";

NOTE: The rndc-keys directory avoids a warning from rndc when rndc.conf and rndc.key exist in the /etc/bind directory. See here.

For DDNS to work, the zone files must live in /var/lib/bind so that the /.jnl files are updatable via dhcpd. The allow-udpate section sets the permitted rndc key. For example:

  zone "" in {
          type master;
          file "/var/lib/bind/";
          allow-update { key rndc-key; };

  zone "" in {
          type master;
          file "/var/lib/bind/db.192.168.0";
          allow-update { key rndc-key; };

Finally, dhcpd has to know the rndc key, so that it is allowed to update DNS. Copy the key stanza (only) from rndc.conf to a file only accessible by dhcpd, e.g. /etc/dhcpd/rndc-keys/rndc.key. Then enable DDNS in /etc/dhcp/dhcpd.conf:

  ddns-updates on;
  ddns-update-style standard;
  ddns-rev-domainname "";
  deny client-updates;
  do-forward-updates on;
  update-optimization off;
  update-conflict-detection off;
  include "/etc/dhcp/rndc-keys/rndc.key";

Restart named and isc-dhcp-server via systemctl and check it works.