# wifi-connect: a captive portal that hands a headless device its WiFi credentials

> wifi-connect turns a Linux device with no keyboard into something a phone can configure, by opening an access point, catching the captive portal, and writing the credentials through NetworkManager. Its real failure mode is hardware: several common WiFi dongles are listed as not working because their drivers do not do access point mode.

**balena-os/wifi-connect** — Easy WiFi setup for Linux devices from your mobile phone or laptop

- Repository: https://github.com/balena-os/wifi-connect
- Stars: 1,511 · Forks: 400
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/balena-os-wifi-connect

## The flow is five steps and the phone is the only interface

The mechanism is a captive portal on an access point the device creates itself. WiFi Connect detects the available WiFi networks, opens an access point, and waits. You connect a phone or laptop to it, the phone detects the captive portal and opens its page, and opening any web page redirects to the portal as well. The portal shows the detected networks as a list and takes a passphrase for the one you want. On submission the access point is disabled, the device tries the network, and if the attempt fails the access point is enabled again for another try. If it succeeds, NetworkManager saves the configuration. That retry is the part that makes the tool usable in the field: a wrong passphrase is not a dead end, you get the portal back.

## Every setting is a command line argument or an environment variable

The network the tool creates is configurable without a config file. The access point SSID is WiFi Connect by default, changed with the --portal-ssid argument or the PORTAL_SSID environment variable, and balenaOS deployments set those through the platform's environment variable management. The network is unprotected by default, which is what makes connecting to it frictionless on a phone with no password prompt. A WPA2 passphrase can be added with --portal-passphrase or PORTAL_PASSPHRASE, and that is the setting to reach for before shipping a device to a customer. The complete list of arguments and environment variables lives in a dedicated command line arguments guide in the repository, and the full application flow is drawn out in a state flow diagram document, which is the right place to start if you are debugging a device that hangs mid flow.

## NetworkManager is the contract, and dhcpcd is a problem

wifi-connect interacts with NetworkManager, which the README says should be the active network manager on the device's host OS. That sentence is the single most important deployment requirement, because the tool does not manage interfaces itself. It asks NetworkManager to scan, to create the access point, to join the chosen network and to persist the result. The same coupling is why distribution support is uneven: the installation section targets systems like Raspbian or Debian, and it says explicitly that Raspbian Stretch uses dhcpcd by default rather than NetworkManager. There is also a balenaOS Docker path, where the same contract has to be satisfied across a container boundary, which takes extra setup described below.

## The Raspbian installer replaces your network manager

For Raspbian Stretch the project ships a shell script that disables dhcpcd, installs NetworkManager as the active network manager, and downloads and installs wifi-connect. The README gives it as a one line command:

```shell
bash <(curl -L https://github.com/balena-io/wifi-connect/raw/master/scripts/raspbian-install.sh)
```

Understand what that line does before running it: it executes a script fetched over the network with root privileges, and part of its job is to stop the network manager your system currently uses. On a device you reach only over the network, disabling dhcpcd before NetworkManager is working is how you lose the link. Download the script, read it, and run it with a serial console or a screen attached. The Docker route below is the safer option for devices you cannot easily reach.

## On balenaOS, a Dockerfile template and a start script do the work

The balenaOS integration uses two shared files. Dockerfile.template manages dependencies, and the example in the repository already contains everything wifi-connect itself needs, so your application's dependencies are what you add. scripts/start.sh holds the commands that run the application, and the README's instruction is to add those commands at the end of the script so everything kicks off once WiFi is correctly configured. That ordering is the point: the app starts only after the device has a network. A Python example of using wifi-connect in a balena project is linked from the README.

## Multicontainer apps need host networking, DBUS and NET_ADMIN

Running wifi-connect in a multicontainer balena application requires three grants, and the README lists them: set the container network mode to host, enable DBUS with the required label and environment variable, and grant Network Admin capabilities. The docker-compose snippet that results is the whole configuration:

```yaml
version: "2.1"

services:
    wifi-connect:
        build: ./wifi-connect
        network_mode: "host"
        labels:
            io.balena.features.dbus: '1'
        cap_add:
            - NET_ADMIN
        environment:
            DBUS_SYSTEM_BUS_ADDRESS: "unix:path=/host/run/dbus/system_bus_socket"
```

Read what you are granting. NET_ADMIN plus host networking plus access to the host system bus is effectively device level control of the network stack, inside a container boundary that suggests isolation it does not provide. It is necessary here because NetworkManager lives on the host, but it belongs in your threat model.

## The dongle list is the real adoption risk

The README names dongles that were successfully tested: TP-LINK TL-WN722N with an Atheros AR9271, ModMyPi with a Ralink RT3070, and ThePiHut with a Ralink RT5370, plus onboard WiFi on a Raspberry Pi 3. From that it draws a reasonable inference that most dongles with Atheros or Ralink chipsets will work. Then it lists the failures, and the reason is stated: the driver is not friendly with access point mode or NetworkManager. Known not to work are the official Raspberry Pi dongle on BCM43143, the Addon NWU276 on Mediatek MT7601, and Edimax on Realtek RTL8188CUS, with the warning that dongles with similar chipsets will probably not work. Check the chipset before you design the provisioning flow, not after a customer reports that no portal appeared.

## It is a Rust binary built against a fork of the NetworkManager crate

The build facts matter if you vendor it. Cargo.toml names the package wifi-connect at version 4.11.90 with a Rust floor of 1.76, and the dependency list is short: clap for arguments, iron with iron-cors, staticfile, mount and router for the portal web server, serde for configuration, log with env_logger, and nix. The notable line is the network-manager dependency, which is not taken from crates.io but from a git repository, github.com/balena-io-modules/network-manager. For reproducible builds, preserve the dependency revision recorded in Cargo.lock. Cross.toml at the repository root indicates cross compilation is set up for multiple targets.

## Conclusion

wifi-connect fits a fleet of headless Linux devices, Raspberry Pi boards in particular, that ship to users or sit in a place with no screen and no keyboard, and it removes the usual alternative of telling someone to attach a screen and type an SSID. Skip it if your WiFi hardware is one of the documented failures, since the official Raspberry Pi dongle, the Addon NWU276 and Edimax units are listed as not working and the cause is the driver rather than the tool. Before deploying, confirm the dongle chipset is Atheros or Ralink, run it on a distribution using NetworkManager rather than dhcpcd, and read the command line arguments guide, because every knob is a flag or an environment variable and none has a config file. The last push was on 2026-10-02 and the Cargo version is 4.11.90.

## FAQ

### Which WiFi dongles work with wifi-connect?

The README lists TP-LINK TL-WN722N on Atheros AR9271, ModMyPi on Ralink RT3070 and ThePiHut on Ralink RT5370 as tested, plus onboard WiFi on a Raspberry Pi 3. The official Raspberry Pi dongle on BCM43143, Addon NWU276 on MT7601 and Edimax on RTL8188CUS are known not to work, because the driver is not friendly with access point mode or NetworkManager.

### How do I change the access point SSID and add a password?

Set the SSID with the --portal-ssid command line argument or the PORTAL_SSID environment variable, and add WPA2 protection with --portal-passphrase or PORTAL_PASSPHRASE. By default the access point SSID is WiFi Connect and the network is unprotected.

### Does wifi-connect need NetworkManager on the device?

Yes. The README states wifi-connect interacts with NetworkManager, which should be the active network manager on the host OS, and the Raspbian Stretch installer disables dhcpcd and installs NetworkManager for that reason.

## Sources

- [balena-os/wifi-connect on GitHub](https://github.com/balena-os/wifi-connect)
- [Issues](https://github.com/balena-os/wifi-connect/issues)
- [License: Apache-2.0](https://github.com/balena-os/wifi-connect/blob/master/LICENSE)
- [README](https://github.com/balena-os/wifi-connect/blob/master/README.md)
- [Releases](https://github.com/balena-os/wifi-connect/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/balena-os-wifi-connect
