# Faucet: an OpenFlow controller for multi table switches

> FAUCET is a Python OpenFlow controller that turns a switch into a routed, VLAN aware network with ACLs, static and BGP routing, and Prometheus and Grafana instrumentation. Its design bets on being hardware agnostic and on the switch agent hiding implementation detail, which makes it attractive for campus and lab networks and awkward for anything needing fine grained table control.

**faucetsdn/faucet** — FAUCET is an OpenFlow controller for multi table OpenFlow 1.3 switches, that implements layer 2 switching, VLANs, ACLs, and layer 3 IPv4 and IPv6 routing.

- Repository: https://github.com/faucetsdn/faucet
- Website: http://faucet.nz
- Stars: 629 · Forks: 201
- Language: Python
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/faucetsdn-faucet

## What Faucet is, and what it is not

FAUCET is an OpenFlow controller for multi table OpenFlow 1.3 switches. It implements layer 2 switching, VLANs, ACLs, and layer 3 IPv4 and IPv6 routing, the latter both static and via BGP.

That sentence covers the whole feature set, and the project is unusually honest about it. The README's list of supported capabilities is short: OpenFlow v1.3 multi table switches including optional table features, hardware and software; multiple datapaths and distributed switching under a single controller; VLANs with mixed tagged and untagged ports; ACLs matching layer 2 and layer 3 fields; IPv4 and IPv6 routing; policy based forwarding to offload to external NFV applications, with 802.1x via hostapd and DHCP to isc DHCPD given as examples; port and flow statistics via Grafana; and controller health and statistics via Prometheus.

The lineage is documented too. FAUCET is based on Waikato University's Valve and on the Ryu OpenFlow Controller, and its design and background are described in ACM Queue.

So this is not a general purpose network operating system. It is a controller for the specific job of making commodity or research switches behave like a configured network, with the configuration living in YAML on the controller rather than in device firmware. The audience is operators of campus networks, lab testbeds and research networks, where switches are physically distributed across a building and reconfiguring them one at a time is not practical.

The phrase in the repository description that matters most for a buyer is multi table. Faucet programs across a pipeline of tables rather than a single one, which is what lets it express ACLs, routing and VLAN handling on switches that have real match capability.

## Hardware agnosticism as the design constraint

Faucet's stated design principle is to be as hardware agnostic as possible and not to require Table Type Patterns. That is a significant architectural choice, and it is worth understanding what it buys and what it costs.

The idea is that the hardware Open Flow Agent, the OFA, hides implementation detail. Faucet does not care which tables are best for certain matches, or whether the switch has special multicast support. It expects the OFA to leverage the right hardware transparently. Faucet emits a logical intent, and the agent works out how to satisfy it on that particular device.

The cost is stated as plainly as the benefit. If you are a hardware vendor who wants to support Faucet, you need to support all the matches in `faucet/faucet_pipeline.py` and pass all tests. That file is the contract, and conformance is defined by a test suite rather than by a marketing claim.

For an operator, this means two things. You can mix switch vendors in one network without maintaining separate pipeline configurations, which is exactly what a campus network with mixed hardware needs. And you give up the ability to tune table placement yourself, which is what a network engineer who has read the vendor's pipeline document would want.

The same approach shows up in the offload mechanism. Policy based forwarding lets Faucet hand traffic decisions to an external NFV application rather than encoding them as more ACL entries, with 802.1x handled by hostapd and DHCP by isc DHCPD as the documented examples. This keeps the controller's own flow table small, at the cost of introducing an external dependency into the path of a packet.

## Running the controller with Prometheus and Grafana

The README does not give installation commands; it points to the installation guide at docs.faucet.nz, along with a configuration guide and a recipe book of configuration snippets for common use cases. The repository, however, ships a docker-compose file that shows the intended deployment shape, and it is a good description of what a running Faucet looks like.

The compose file defines four services. Prometheus and Grafana hold the metrics, gauge is a helper that exposes them, and faucet is the controller itself. The controller service is built from Dockerfile.faucet and exposes the OpenFlow port 6653:

```yaml
    faucet:
        restart: always
        build:
            context: .
            dockerfile: Dockerfile.faucet
        image: 'faucet/faucet:latest'
        volumes:
            - '${FAUCET_PREFIX}/var/log/faucet:/var/log/faucet'
            - '${FAUCET_PREFIX}/etc/faucet:/etc/faucet'
        ports:
            - '6653:6653'
            - '9302'
        environment:
            FAUCET_CONFIG_STAT_RELOAD: '${FAUCET_CONFIG_STAT_RELOAD}'
            FAUCET_EVENT_SOCK: '${FAUCET_EVENT_SOCK}'
            FAUCET_EVENT_SOCK_HEARTBEAT: '${FAUCET_EVENT_SOCK_HEARTBEAT}'
```

Two things stand out in that snippet. Configuration and logs are bind mounted from directories under `${FAUCET_PREFIX}`, so a deployment keeps its YAML and its log outside the container. And three environment variables are passed through from the host, including a configuration stat reload setting and the event socket with a heartbeat, which is how the controller is told to watch the config file for changes rather than restarted on every edit.

The Python side has its own requirements, and they are pinned tightly for the components that matter:

```
os_ken==3.1.0
prometheus_client==0.26.0
pytricia>=1.0.0
```

os-ken is the OpenFlow library, prometheus_client exposes the metrics, and pytricia is the trie used for the forwarding lookups.

## Python 3.11 is the floor, and the installer enforces it

The setup script refuses to install on an old interpreter, and it does so with an explicit message rather than a traceback. The check runs at install time and exits if the interpreter is below the supported version.

The message in the script is worth reading:

```
    Faucet 1.9.0 and above are no longer compatible with older versions of python 3.

    Please upgrade to python 3.11 or newer.
```

So the supported floor is Python 3.11, and it has been that since Faucet 1.9.0. The script also rejects Python 2 outright with its own message.

The installer does more than check versions. It installs configuration files into the system: it creates /etc/faucet/, places ryu.conf there, and creates /var/log/faucet/. It also handles migration from the older layout, checking for the previous /etc/ryu/ryu.conf and the /etc/ryu/faucet/ directory and copying them into the new locations when they are present.

That last detail tells you something about the project's history. The configuration path moved from a Ryu-centric layout to a Faucet-centric one, and the installer was written to carry existing deployments across rather than making operators do it by hand. It also means the setup step wants write access to system directories, so a deployment into a restricted environment either needs root for the install step or a different packaging route. The repository carries a debian/ directory, which suggests packaging for Debian based systems is a supported path as well.

## Testing with mininet and Open vSwitch

The test story is the part of this project that makes it credible for a network that people depend on.

The README states that unit and systems tests run under GitHub workflows based on mininet and OVS. That combination is the standard way to test a controller without physical hardware: mininet provides a virtual network of software switches and hosts, and Open vSwitch provides the OpenFlow capable datapath. The two together let a CI run exercise the controller against real OpenFlow messages, real table misses and real packet flow, rather than against mocks.

The badges in the README confirm both suites run in CI, one for unit tests and one for integration tests, and there is a separate code coverage service badge. The repository also carries a pyrightconfig.json and a .pylintrc, and the Makefile has explicit targets for code formatting with yapf, static analysis with pylint, and generating UML diagrams with pyreverse. There are dedicated Dockerfiles for the test run and for fuzzing, with separate fuzz requirements for fuzzing the packet handling, which is the part of a controller most worth fuzzing.

For a vendor trying to support Faucet, the same infrastructure is a gift. The rule is that a vendor must support all the matches in the pipeline file and pass all tests, and the ability to run that suite is what makes the rule enforceable. Vendor guides are published on readthedocs under a vendors section.

The one thing the README does not describe is a conformance certification process or a list of certified vendors, so the practical meaning of support is that the project maintains guides and tests rather than that it grants approval.

## Where Faucet stops being the right controller

The first limitation is the one the project states as a principle: no Table Type Patterns and hardware agnosticism. If your network depends on placing a specific rule in a specific table, on a specific hardware pipeline you have reverse engineered, Faucet will not do it, and the fix is not a configuration change.

The second is the scope of the feature set. Switching, VLANs, ACLs and IP routing with BGP is a lot for a controller, but it is not a full routing protocol suite, and it is not a network automation layer. Anything beyond that list is out of scope unless the README says otherwise, and it does not.

The third is the operational shape. Faucet is a controller, which means the network's forwarding behaviour depends on a running service. When the controller is down or being reloaded, switches keep their existing flows but no new configuration reaches them, and the compose file's three environment variables for config reload and event socket heartbeat show that this part of the design is deliberate rather than incidental. For a network where the controller is a single point of failure, that is a real risk to plan for, and the README does not describe a controller high availability mode.

The fourth is the Python version floor, which matters for the operating system you can run it on. Python 3.11 rules out the older distributions still common in long-lived campus environments, so before adopting, check what your network management hosts are running.

The alternative approach to the same problem is to put the routing and switching configuration into the switches themselves with a vendor operating system, or to use a controller that programs tables directly and therefore gives you full control at the cost of per-vendor work. The difference in approach is exactly where Faucet takes its position: one controller, one configuration language, many switch models, at the price of table-level control.

## Licence, releases and the shape of the repository

Faucet is released under the Apache License 2.0, with the LICENSE file at the top of the repository. Apache-2.0 allows commercial use, modification and distribution with attribution and patent rights, provided the licence text and notices are kept.

The release history shows a project that ships steadily but not on a fixed calendar. Version 1.10.12 was released on 2026-03-05, preceded by 1.10.11 on 2024-04-11 and 1.10.10 on 2023-10-26. The repository is not archived and the last push was on 2026-10-22. In other words, the tree has seen work since the most recent tag, so anyone deploying from source should expect to be ahead of the published release rather than on it.

The tree itself describes the scope. The `faucet/` directory holds the controller, `adapters/` and `clib/` hold the hardware-facing and native pieces, `tests/` the test suites, `docker/` the container builds, `debian/` the packaging, and `docs/` the documentation that the README links to. Two files at the top level stand out: `hw_switch_config.yaml`, which is a real switch configuration example, and `ofctl_rest/`, which exposes OpenFlow control over REST rather than only over the OpenFlow channel, a useful addition for operating a network.

Community communication is conventional and healthy for a project of this age: a mailing list on Google Groups for announcements, an IRC channel on libera, tutorial videos on YouTube, a developer blog, and a bug tracker for issues. The authors file is in the repository as AUTHORS.

## Conclusion

Adopt FAUCET when you need a routed, VLAN aware network with ACLs and BGP from switches you already own, and when you can live with a controller that deliberately refuses to program tables directly. Do not adopt it for a network that needs per table flow placement, vendor specific pipeline control, or a full feature set beyond switching, VLANs, ACLs and IP routing, since the README states the design principle is hardware agnosticism with no Table Type Patterns. Before installing, read the installation guide at docs.faucet.nz, confirm your switch agent implements OpenFlow 1.3 multi table, and decide which monitoring path you want, since the bundled docker-compose brings up Prometheus and Grafana alongside the controller.

## FAQ

### What is Faucet and what does it implement?

FAUCET is an OpenFlow controller for multi table OpenFlow 1.3 switches that implements layer 2 switching, VLANs, ACLs and layer 3 IPv4 and IPv6 routing, both static and via BGP. It is based on Waikato University's Valve and the Ryu OpenFlow Controller.

### Which Python version does Faucet require?

Python 3.11 or newer. The setup script refuses to install on older interpreters and prints that Faucet 1.9.0 and above are no longer compatible with older versions of Python 3.

### How is Faucet installed?

The README refers to the installation guide at docs.faucet.nz rather than listing commands. The repository also ships a docker-compose file that starts the controller together with Prometheus, Grafana and a gauge helper, with configuration and logs bind mounted from directories under a FAUCET_PREFIX path.

### Does Faucet support hardware switches from different vendors?

Its design principle is to be as hardware agnostic as possible and not to require Table Type Patterns, with the hardware Open Flow Agent hiding implementation detail. A vendor wanting support must implement all the matches in `faucet/faucet_pipeline.py` and pass all tests.

### How is Faucet tested?

Unit and systems tests run under GitHub workflows based on mininet and OVS, so the controller is exercised against virtual OpenFlow switches rather than hardware. The repository also carries coverage reporting, linting configuration and Dockerfiles for the test run and for fuzzing.

### What monitoring does Faucet provide?

Port and flow statistics are exposed via Grafana, and controller health and statistics via Prometheus. The bundled docker-compose file defines both services along with a gauge helper and the controller itself.

## Sources

- [faucetsdn/faucet on GitHub](https://github.com/faucetsdn/faucet)
- [License: Apache-2.0](https://github.com/faucetsdn/faucet/blob/main/LICENSE)
- [Project website](http://faucet.nz)
- [README](https://github.com/faucetsdn/faucet/blob/main/README.md)
- [Releases](https://github.com/faucetsdn/faucet/releases)

---

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