Setting up a Raspberry Pi AP (Access Point) on Raspberry Pi OS Bookworm requires a fundamental shift from older tutorials. Because Bookworm deprecated dhcpcd in favor of NetworkManager, legacy hostapd and dnsmasq configurations will fail or conflict with the OS network stack. The direct answer for a stable, modern Raspberry Pi AP is to use a Routed NAT topology powered by systemd-networkd for DHCP/masquerading and hostapd for the WiFi radio, while explicitly unmanaging the wireless interface from NetworkManager.

This guide targets the Raspberry Pi 5 (4GB) and Raspberry Pi 4 Model B running Raspberry Pi OS Bookworm (64-bit). By the end, you will have a WPA3-transition access point with an automatic hardware status LED.

Decision Tree: Bridged vs. Routed AP Topology

Before writing configuration files, you must choose how your Pi will route traffic. Most hobbyists default to bridged mode, but routed NAT is vastly superior for embedded and IoT deployments.

Criteria Bridged AP (Layer 2) Routed NAT AP (Layer 3)
Subnet Shares main router's subnet Creates isolated Pi-managed subnet
DHCP Server Main Router (requires MAC filtering/reservations) Pi (systemd-networkd handles it locally)
IoT Isolation None (IoT devices can see your main LAN) High (IoT devices only see the Pi gateway)
Setup Complexity High (requires bridging wlan0 to eth0, fighting NM) Low (native systemd-networkd masquerade)
Decision Verdict: Choose Routed NAT. If your goal is to connect ESP32 sensors, Arduino IoT boards, or isolated lab equipment to a local Pi server (like Home Assistant or an MQTT broker), Routed NAT prevents IP conflicts with your main home router and keeps noisy IoT broadcast traffic off your primary LAN.

Hardware & Parts List (2026 Standard)

Do not use underpowered supplies; the Pi 5 will brownout and drop the WiFi radio under AP transmit loads if starved of current.

  • Compute: Raspberry Pi 5 (4GB) or Raspberry Pi 4 Model B (4GB)
  • Power: Official Raspberry Pi 27W USB-C Power Supply (for Pi 5) or 15W USB-C (for Pi 4)
  • Uplink: Cat6 Ethernet Cable (hardwired to your main router/switch)
  • Storage: 32GB+ NVMe SSD via PCIe HAT (Pi 5) or A2-rated microSD (Pi 4)
  • Status LED: 5mm Green Diffused LED + 220Ω 1/4W Resistor
  • Wiring: 2x Female-to-Male Jumper Wires

Network Interface & GPIO Pin Mapping

Since this project relies on network interfaces rather than raw sensor GPIO, the mapping below defines the logical network boundaries and the physical hardware status indicator.

Interface / Component Designation Role in AP Build
Ethernet Port eth0 (or end0 on Pi 5) WAN Uplink (DHCP Client to main router)
Built-in WiFi wlan0 AP Radio (Static IP, DHCP Server, NAT)
GPIO 21 Physical Pin 40 Status LED Anode (via 220Ω resistor)
Ground Physical Pin 39 Status LED Cathode

Step-by-Step Configuration (Bookworm Native)

This procedure bypasses the deprecated dhcpcd and uses systemd-networkd's built-in IP masquerading, eliminating the need for complex iptables or nftables scripting.

1. Isolate wlan0 from NetworkManager

NetworkManager will aggressively try to connect wlan0 to known networks as a client. You must tell it to ignore the interface so hostapd can claim the radio.

sudo nmcli device set wlan0 managed no
sudo nmcli radio wifi off

2. Enable systemd-networkd

Enable the native systemd network daemon to handle the AP subnet and DHCP.

sudo systemctl enable systemd-networkd
sudo systemctl start systemd-networkd

3. Configure the AP Subnet and NAT

Create the network definition file. The IPMasquerade=ipv4 directive is the modern Bookworm method for NAT routing.

sudo nano /etc/systemd/network/10-wlan0.network

Paste the following configuration:

[Match]
Name=wlan0

[Network]
Address=192.168.4.1/24
DHCPServer=yes
IPMasquerade=ipv4

[DHCPServer]
PoolOffset=10
PoolSize=50
EmitDNS=yes
DNS=1.1.1.1

4. Configure hostapd for WPA3-Transition

WPA3 (SAE) is mandatory for modern security, but older ESP8266/ESP32 boards only support WPA2. We use transition mode to support both. For full parameter details, refer to the canonical hostapd configuration reference.

sudo nano /etc/hostapd/hostapd.conf
country_code=US
interface=wlan0
ssid=FluxLab_IoT
hw_mode=g
channel=6
ieee80211n=1
wmm_enabled=1

# Security: WPA3-SAE Transition Mode
wpa=2
wpa_key_mgmt=SAE WPA-PSK
wpa_pairwise=CCMP
rsn_pairwise=CCMP
ieee80211w=2
sae_require_mfp=1

wpa_passphrase=YourSecurePassword123!

Note: Change country_code to your local ISO code. Operating without this violates RF regulations and blocks 5GHz channels.

5. Point hostapd to the Config and Enable

Edit the default hostapd daemon file to load your config:

sudo nano /etc/default/hostapd

Uncomment and set: DAEMON_CONF="/etc/hostapd/hostapd.conf"

sudo systemctl unmask hostapd
sudo systemctl enable hostapd
sudo systemctl start hostapd

The Python Status Monitor (Complete Code)

Headless Pis need physical feedback. This Python script uses gpiozero to monitor the hostapd service state. If the AP drops, the LED blinks rapidly; if running, it stays solid.

#!/usr/bin/env python3
"""
Raspberry Pi AP Status Monitor
Target: Raspberry Pi 5 / 4 (Bookworm)
Hardware: LED on GPIO 21 (Pin 40) via 220 ohm resistor
"""

import subprocess
import time
from gpiozero import LED
from signal import pause

# Pin 40 maps to GPIO 21
STATUS_LED = LED(21)

def check_hostapd_status():
    """Returns True if hostapd is actively running."""
    try:
        result = subprocess.run(
            ['systemctl', 'is-active', 'hostapd'],
            capture_output=True,
            text=True,
            check=False
        )
        return result.stdout.strip() == 'active'
    except Exception as e:
        print(f"Error checking service: {e}")
        return False

def main():
    print("Starting AP Status Monitor on GPIO 21...")
    try:
        while True:
            if check_hostapd_status():
                # Solid ON for healthy AP
                STATUS_LED.on()
                time.sleep(2)
            else:
                # Rapid blink for AP failure
                STATUS_LED.blink(on_time=0.2, off_time=0.2, n=5, background=False)
                time.sleep(1)
    except KeyboardInterrupt:
        STATUS_LED.off()
        print("Monitor stopped.")

if __name__ == "__main__":
    main()

Save this as /home/pi/ap_monitor.py, make it executable (chmod +x), and create a systemd service to run it on boot so it survives reboots without requiring user login.

Debugging: "nl80211: Could not configure driver mode"

If you run sudo hostapd -d /etc/hostapd/hostapd.conf and immediately see this exact error string, the kernel is refusing to put the WiFi chip into Master/AP mode.

Exact Error String:
nl80211: Could not configure driver mode
wlan0: Could not connect to kernel driver

The First Three Things to Check (Ranked by Probability):

  1. NetworkManager Interference (90% of cases): NetworkManager is still holding the interface.
    Fix: Run sudo nmcli device set wlan0 managed no and reboot.
  2. RFKill Soft Block (8% of cases): The OS has soft-blocked the WiFi radio to save power.
    Fix: Run sudo rfkill unblock wifi and verify with rfkill list.
  3. Missing or Invalid Country Code (2% of cases): The regulatory domain is unset, preventing the driver from initializing the radio frequencies.
    Fix: Ensure country_code=US (or your region) is at the very top of hostapd.conf.

For deeper diagnostics on Bookworm networking shifts, consult the official Raspberry Pi configuration documentation.

Extending and Simplifying the Build

Depending on your deployment environment, you may need to alter the complexity of this build.

How to Simplify (The NetworkManager Native Route)

If you do not need WPA3, custom DHCP ranges, or advanced hostapd parameters, you can skip systemd-networkd and hostapd entirely. NetworkManager can create a basic WPA2 hotspot in one command:

sudo nmcli connection add type wifi ifname wlan0 con-name FluxAP autoconnect yes ssid FluxLab_Simple
sudo nmcli connection modify FluxAP 802-11-wireless.mode ap 802-11-wireless.band bg ipv4.method shared
sudo nmcli connection modify FluxAP wifi-sec.key-mgmt wpa-psk wifi-sec.psk "SimplePass123"
sudo nmcli connection up FluxAP

Trade-off: This uses WPA2 only, offers no granular control over beacon intervals, and uses the 10.42.x.x shared subnet which is harder to route statically.

How to Extend (Adding DNS Filtering)

To turn this Raspberry Pi AP into a network-wide ad blocker or DNS sinkhole, install Pi-hole. Because we used Routed NAT, Pi-hole will automatically intercept DNS requests from the 192.168.4.x subnet.
Extension Step: In the [DHCPServer] block of your 10-wlan0.network file, change DNS=1.1.1.1 to DNS=192.168.4.1. This forces all connected IoT clients to use the Pi's local Pi-hole instance for name resolution before traffic is masqueraded out to the internet.