Batfish guide

Batfish, explained: validating network configurations before they reach production

Batfish is an open source network configuration analysis tool that finds errors and checks the correctness of planned or current configurations. This guide covers how it works, how to run it, and how to ask it questions with Python.

Batfish at a glance

Batfish is a network configuration analysis tool that can find bugs and guarantee the correctness of (planned or current) network configurations. That is how the project describes itself on GitHub, and it is the sense of the name that matters here: this guide is about Batfish the network configuration tool, which reads device configuration files and answers questions about how the network they describe will behave.

Batfish checks the properties you ask about, within its model of the configurations you supply. Results depend on the snapshot being complete and on support for the features involved, and they do not establish the live state of the network.

Licence
Apache-2.0 (licence text)
pybatfish, latest release
2026.9.17.3748, released 18 September 2026 (release page on PyPI). The previous release was 2026.8.19.3660
Python
3.10 or newer. PyPI lists Python 3.10 to 3.14
Batfish service, latest tagged release
v2026.08.27, published 27 August 2026 (release on GitHub)
Maintainers on PyPI
arifogel, dhalperi, intentionet, ratul
Links
batfish.org · GitHub · Docs · PyPI

Verified on against PyPI, batfish.org, the Batfish and pybatfish documentation, the GitHub repositories and the Docker Hub pages for the official images. The maintainer names are those listed on the pybatfish PyPI page on that date. The code examples are written for pybatfish 2026.9.17.3748 and checked against its source. None was run against a Batfish service.

What is Batfish?

Every network change carries the same worry: will this configuration do what we think it does, and what else will it break? Batfish answers that before the change reaches a device. It reads the configuration files of your routers, switches and firewalls, converts them into a vendor-neutral model of the network, works out how traffic will be forwarded, and then lets you ask questions about the result. The project's documentation puts the aim as providing correctness guarantees for security, reliability and compliance by analysing device configurations.

The checks it supports fall into four groups in the project's own description:

  • Configuration compliance. Structures that are referenced but not defined, settings such as MTU, AAA, NTP and logging that must match a template, and devices that can only be reached with SSHv2.
  • Reliability. Whether end-to-end reachability survives any single link or device failure, and whether services such as DNS are reachable everywhere.
  • Security. Whether sensitive services can be reached only from the subnets or devices intended, and whether paths cross a firewall.
  • Change analysis. Whether reachability is identical across the current and a planned configuration, whether an ACL or firewall change has collateral effects, and whether two configurations are functionally equivalent.

One property is easy to miss and shapes how you use it. The Batfish README states that it does not require direct access to network devices: the core analysis needs only the configuration files. So Batfish is a network validation tool that works on files, which makes it easy to run in a pipeline, a lab or a laptop. Offline analysis does not connect to or change your devices. Optional extras, such as BGP routes received from external peers or topology learned from LLDP or CDP, can enrich the model.

If you are choosing among tools that touch the network, it helps to place Batfish by what it does not do. It is not a connection library like Netmiko or Paramiko, it is not a task runner like Nornir, and it does not change devices. It analyses configuration and tells you what it finds.

Where Batfish came from and who maintains it

Batfish began as research. batfish.org says it was originally developed by researchers at Microsoft Research, UCLA and USC, and was later enhanced and maintained by Intentionet. On the same page, batfish.org says: Since the Intentionet team joined AWS, it has been an AWS-managed open source project under the same license (Apache 2.0). It adds that many others have contributed.

The papers

Two papers describe the work, and both are worth reading:

  • A General Approach to Network Configuration Analysis. Ari Fogel, Stanley Fung, Luis Pedrosa, Meg Walraed-Sullivan, Ramesh Govindan, Ratul Mahajan and Todd Millstein. 12th USENIX Symposium on Networked Systems Design and Implementation (NSDI 15), Oakland, May 2015. The original research paper.
  • Lessons from the evolution of the Batfish configuration analysis tool. Matt Brown, Ari Fogel, Daniel Halperin, Victor Heorhiadi, Ratul Mahajan and Todd Millstein. Proceedings of the ACM SIGCOMM 2023 Conference, September 2023 (DOI). Its abstract describes how Batfish evolved from a research prototype to an industrial-strength product, including how Datalog had significant limitations for generating and analysing forwarding state and how binary decision diagrams proved highly versatile.

Maintainers and activity

PyPI lists four maintainer accounts for pybatfish: arifogel, dhalperi, intentionet, ratul. The github.com/batfish organisation hosts the service, the Python client, the Docker images and the documentation, and its contributor lists show how many people have worked on them. The project is active: the main repository had commits on 30 September and 2 October 2026, the official images were rebuilt on 1 October 2026, and the latest tagged service release is v2026.08.27 (27 August 2026). pybatfish 2026.9.17.3748 followed 2026.8.19.3660 on 18 September 2026.

Batfish is serious, research-grounded software. If you rely on it, read the papers, follow the repository and join the project's Slack community.

How Batfish works

There are four ideas to hold in your head. Once you have them, the documentation and the example notebooks read easily.

How configurations become answers in BatfishFour stages from top to bottom. Device configuration files go in as a snapshot. The Batfish service parses them into a vendor-neutral model and computes the data plane. You ask questions through pybatfish. Answers come back as tables that convert to pandas DataFrames.Configs inA snapshot: a folder of device configuration filesModelThe service parses each vendor format and computes routing and forwardingQuestionsAsked from Python through pybatfish, or from the example notebooksAnswers outTables you can filter as pandas DataFrames, or assert on
Configurations go in, the service builds the model, you ask questions, and the answers come back as data.

Networks and snapshots

The pybatfish documentation defines a network as a logical grouping of devices, which may be all of yours or one data centre, and a snapshot as the state of the network at a given time. A network can hold many snapshots, so you can compare how it evolves. Batfish expects a snapshot to be a folder with a configs subfolder for device configurations and, optionally, a batfish subfolder for supplemental information that is not a device configuration. The project's packaging guide covers the special formats for some platforms.

The snapshot folder layout Batfish expects, and the two example snapshots
snapshot/
  configs/
    router1.cfg
    router2.cfg
  batfish/
    isp_config.json

# The example network holds two snapshots, each with this layout:
networks/example/live/configs/
networks/example/candidate/configs/

The service and the client

Batfish itself is a service, written in Java, that listens for requests. pybatfish is the Python client you use to talk to it. The service can run on your own machine or on a remote server, and one service can hold many networks and snapshots.

Questions and answers

You do not query raw configuration text. You ask a question, such as nodeProperties, routes or traceroute, and the service returns an answer. The pattern in the documentation is bf.q.<question>() to create the question, .answer() to run it, and .frame() to turn the answer into a pandas DataFrame, which you filter and process like any other table.

Differential analysis

Most questions can also be run differentially, by passing snapshot and reference_snapshot to .answer(). This compares two snapshots, for example the current configurations against a planned change, and shows only the differences in behaviour. The docs describe two questions that are exclusively differential: compareFilters and differentialReachability.

Running Batfish

The project ships two official Docker images, both documented in the batfish/docker repository. The getting-started documentation tells you to pull and run the latest batfish/allinone image, and the Batfish README says the same. It bundles the Batfish service, pybatfish and example Jupyter notebooks, which makes it the quick way to begin (Docker Hub). The batfish/batfish image holds the core service only, and suits a server that other people or pipelines connect to (Docker Hub).

Run the all-in-one image (service, pybatfish and example notebooks)
docker pull batfish/allinone
docker run --name batfish -v batfish-data:/data \
  -p 127.0.0.1:8888:8888 -p 127.0.0.1:9997:9997 -p 127.0.0.1:9996:9996 \
  batfish/allinone

Port 8888 serves Jupyter, and 9997 and 9996 are the Batfish service ports that pybatfish connects to. The commands publish them on 127.0.0.1 only, so the service is reachable from the same machine and not from the network. Remote access needs separate, deliberate configuration, and the service should not be exposed to an untrusted network. For a server that your own pipelines use, run the service-only image and keep its data on the host:

Run the service-only image, keeping data on the host
mkdir -p data
docker run --name batfish -v "$(pwd)/data":/data \
  -p 127.0.0.1:9997:9997 -p 127.0.0.1:9996:9996 \
  batfish/batfish

Then install pybatfish. The documentation recommends a Python 3 virtual environment, and pybatfish 2026.9.17.3748 needs Python 3.10 or newer.

Install pybatfish into a virtual environment
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pybatfish
python -c "import pybatfish, importlib.metadata as m; print(m.version('pybatfish'))"

Sizing

The documentation says the example notebooks need a reasonably capable laptop: a dual-core CPU, 8 GB of RAM and 256 GB of disk. For your own network it recommends a server with at least a quad-core CPU with two threads per core, 32 GB of RAM and 256 GB of disk. The memory available to Batfish is set by your Docker configuration. Measure what your own snapshots need, give the container enough memory, and set a sensible --memory limit, as the Docker documentation advises for any container.

Upgrading

Pulling a new image does not change a container that is already running. The getting-started documentation upgrades by stopping and removing the container, pulling the image, and creating a new container from it with docker run, keeping the data volume:

Upgrade by recreating the container from the new image
# Pulling an image does not change a container that is already running.
# Pull the new image, remove the old container, then create a new one from
# the new image. The named volume keeps your data.
docker pull batfish/allinone
docker stop batfish
docker rm batfish
docker run --name batfish -v batfish-data:/data \
  -p 127.0.0.1:8888:8888 -p 127.0.0.1:9997:9997 -p 127.0.0.1:9996:9996 \
  batfish/allinone

The documentation recommends that you upgrade Batfish and pybatfish together, and warns that snapshots you uploaded earlier can become incompatible with a newer Batfish and may need to be initialised again.

pybatfish with code

Examples written for pybatfish 2026.9.17.3748 and checked against its source. Run them against a lab network before production. Question names, parameters and result columns were checked against the Batfish question definitions at commit 88f5ed2 on 2 October 2026. None was run against a Batfish service.

Get the example network

The snapshots used throughout are the project's example network, a school campus with two upstream providers. It lives in the batfish repository under networks/example. Its live snapshot holds the configurations currently deployed, and its candidate snapshot is a planned change that, according to the network's README, contains an error: an access group applied in the wrong direction. Each snapshot has a configs folder, the layout shown earlier, so the differential example needs no preparation beyond cloning. Clone the repository and run every script from its root directory:

Get the example network and choose where to run the scripts
# The example network lives in the batfish repository, under networks/example.
git clone --depth 1 https://github.com/batfish/batfish.git
cd batfish

# Run every script in this guide from this directory (the repository root),
# so that paths such as networks/example/live resolve.
ls networks/example

For your own network, build the same layout: one folder per snapshot, each with a configs folder inside. The scripts read the service address from the environment:

Point the scripts at the Batfish service
# Leave this unset to use localhost, which is where the commands above publish
# the ports. Use another address only after you have deliberately configured
# and secured remote access to the service.
export BATFISH_HOST="localhost"

Connect and create a snapshot

Session is the entry point. By default it contacts the service as soon as you create it, to load the list of questions the service offers, so the service has to be running first. set_network selects or creates a network, and init_snapshot uploads a folder or zip file, parses it and names the snapshot. Pass overwrite=True to replace a snapshot of the same name. In a shared CI system, use names that are unique to each run: with fixed names and overwrite=True, concurrent jobs replace each other's inputs.

Connect, then create a network and a snapshot
import os

from pybatfish.client.session import Session

# The Batfish service listens on 9997 (v1) and 9996 (v2) by default.
bf = Session(host=os.environ.get("BATFISH_HOST", "localhost"))

bf.set_network("example_network")
bf.init_snapshot("networks/example/live", name="current", overwrite=True)

Check the parse before you trust any answer

Batfish may not support every line in your configurations. The documentation recommends checking the snapshot you just initialised with initIssues, and fileParseStatus shows which files parsed fully.

Check what Batfish could not parse
import os

from pybatfish.client.session import Session

bf = Session(host=os.environ.get("BATFISH_HOST", "localhost"))
bf.set_network("example_network")
bf.set_snapshot("current")

# Which files parsed completely, and which did not.
status = bf.q.fileParseStatus().answer().frame()
print(status[status["Status"] != "PASSED"])

# Lines Batfish did not recognise, and other warnings from conversion.
print(bf.q.initIssues().answer().frame())
print(bf.q.parseWarning().answer().frame())

Ask questions of the model

These are a handful of the questions the project's getting-started notebook and documentation use. Each returns a table.

Ask questions of the model
import os

from pybatfish.client.session import Session
from pybatfish.datamodel import HeaderConstraints

bf = Session(host=os.environ.get("BATFISH_HOST", "localhost"))
bf.set_network("example_network")
bf.set_snapshot("current")

# Vendor-neutral properties of nodes whose names contain "border".
props = bf.q.nodeProperties(
    nodes="/border/", properties="Domain_Name,NTP_Servers,Interfaces"
).answer().frame()
print(props)

# Structures that are referenced but never defined (an ACL, a route map).
print(bf.q.undefinedReferences().answer().frame())

# BGP sessions that are configured but not established.
sessions = bf.q.bgpSessionStatus().answer().frame()
print(sessions[sessions["Established_Status"] != "ESTABLISHED"])

# ACL or firewall lines that can never match, and the lines that block them.
print(bf.q.filterLineReachability().answer().frame())

# Computed routes for one prefix on the core routers.
print(bf.q.routes(nodes="/core/", network="90.90.90.0/24").answer().frame())

# A virtual traceroute from a host, computed offline from the configs.
trace = bf.q.traceroute(
    startLocation="host1", headers=HeaderConstraints(dstIps="1.0.2.2")
).answer().frame()
print(trace)
  • nodeProperties extracts vendor-neutral properties such as NTP servers; undefinedReferences lists structures that are referenced but not defined.
  • bgpSessionStatus reports whether configured BGP sessions would be established, and filterLineReachability finds ACL and firewall lines that can never match, with the lines that block them.
  • routes returns the computed routing tables, and traceroute follows a flow through the network from a start location, using HeaderConstraints to describe the packet. The documentation notes that the first question that needs the data plane can take a few minutes on a large network, and that later ones are quick.

Compare two snapshots

This is where Batfish earns its place in a change process. Initialise the current and the candidate configurations as two snapshots in the same network, then ask differential questions.

Compare a candidate snapshot with the current one
import os

from pybatfish.client.session import Session

bf = Session(host=os.environ.get("BATFISH_HOST", "localhost"))
bf.set_network("example_network")

bf.init_snapshot("networks/example/live", name="current", overwrite=True)
bf.init_snapshot("networks/example/candidate", name="candidate", overwrite=True)

# Filters whose lines treat the same flow differently in the two snapshots.
filter_changes = bf.q.compareFilters().answer(
    snapshot="candidate", reference_snapshot="current"
).frame()
print(filter_changes)

# Flows that succeed in one snapshot and not in the other.
reach_changes = bf.q.differentialReachability().answer(
    snapshot="candidate", reference_snapshot="current"
).frame()
print(reach_changes)

# Most questions can be run differentially the same way.
print(bf.q.routes().answer(snapshot="candidate", reference_snapshot="current").frame())

Batfish in a change pipeline

The Batfish README calls pre-deployment validation a critical gap in existing network automation workflows, and says that including Batfish in them lets you ensure that only correct changes are deployed. The shape is the same whatever tool generates the change or deploys it:

  1. Generate candidate configurations. From templates, a source of truth, or a proposed edit, write the intended configuration files into a folder with a configs subfolder.
  2. Snapshot. Initialise that folder as a snapshot in Batfish. Keep the current configurations as a second snapshot if you want a differential check.
  3. Validate the input. Check that every device you expect is in the snapshot, and that Batfish parsed and converted the files without issues the team has not reviewed. An answer from an incomplete or half-parsed snapshot proves little.
  4. Ask questions. Run the checks your network needs: no undefined references, BGP sessions that are configured to come up and would establish, no forwarding loops, no reachability change you did not intend.
  5. Fail on violations. If any check fails, the step exits with a non-zero status, the pipeline stops, and the change goes no further.

pybatfish includes assertion helpers for this. They are available as bf.asserts on a session, they take a snapshot argument, and by default they raise BatfishAssertException when a check does not hold. Passing soft=True makes a failure a warning instead. Two of them cover BGP and are easy to confuse. assert_no_unestablished_bgp_sessions looks only at sessions that are compatible by configuration, and fails if any of them would not establish because of routing or forwarding problems. assert_no_incompatible_bgp_sessions finds the sessions whose configuration does not match the peer's. Both are predictions from the model, not observations of live sessions.

The script below validates the input first. It compares the nodes in the snapshot with an expected list, and fails on any initIssues row that is not in a small allow-list of reviewed exceptions and on any file that fileParseStatus does not report as PASSED. Only then does it run the assertions, exiting with status 1 on the first failure. It names the network and snapshot after the CI job, so concurrent runs do not collide, and deletes the network afterwards. A pass means the selected checks passed for that snapshot, nothing more.

Gate a pipeline step: validate the input, then run the checks
import os
import sys

from pybatfish.client.session import Session
from pybatfish.exception import BatfishAssertException

CANDIDATE_DIR = sys.argv[1]  # a directory with a configs/ folder inside

# Give every run its own network and snapshot names, for example from the job
# ID that your CI sets. With fixed names and overwrite=True, concurrent jobs
# replace each other's inputs.
RUN_ID = os.environ.get("CI_JOB_ID", "local")
NETWORK = f"ci-{RUN_ID}"
SNAPSHOT = f"candidate-{RUN_ID}"

# Every device the candidate snapshot must contain.
EXPECTED_NODES = {
    "as1border1", "as1border2", "as1core1",
    "as2border1", "as2border2", "as2core1", "as2core2",
    "as2dept1", "as2dist1", "as2dist2",
    "as3border1", "as3border2", "as3core1",
}

# Reviewed exceptions: (Type, Details) pairs from initIssues that the team
# has read and accepted. Anything not listed here fails the run.
ACCEPTED_ISSUES: set[tuple[str, str]] = set()


def fail(message: str) -> None:
    print(message)
    sys.exit(1)


bf = Session(host=os.environ.get("BATFISH_HOST", "localhost"))
bf.set_network(NETWORK)
try:
    bf.init_snapshot(CANDIDATE_DIR, name=SNAPSHOT, overwrite=True)

    # Input checks first: the answers below only mean something if the
    # snapshot is complete and was parsed.
    nodes = bf.q.nodeProperties(properties="Hostname").answer(snapshot=SNAPSHOT).frame()
    missing = EXPECTED_NODES - set(nodes["Node"])
    if missing:
        fail(f"Nodes missing from the snapshot: {sorted(missing)}")

    issues = bf.q.initIssues().answer(snapshot=SNAPSHOT).frame()
    unaccepted = [
        row for _, row in issues.iterrows()
        if (row["Type"], row["Details"]) not in ACCEPTED_ISSUES
    ]
    if unaccepted:
        fail(f"{len(unaccepted)} unreviewed parse or conversion issue(s):\n{issues}")

    parse = bf.q.fileParseStatus().answer(snapshot=SNAPSHOT).frame()
    not_passed = parse[parse["Status"] != "PASSED"]
    if len(not_passed) > 0:
        fail(f"Files that did not parse completely:\n{not_passed}")

    # Behavioural checks. Each one is a prediction from the model.
    try:
        bf.asserts.assert_no_undefined_references(snapshot=SNAPSHOT)
        # Sessions that are compatible by configuration but would not establish.
        bf.asserts.assert_no_unestablished_bgp_sessions(snapshot=SNAPSHOT)
        # Sessions whose configuration does not match the peer's.
        bf.asserts.assert_no_incompatible_bgp_sessions(snapshot=SNAPSHOT)
        bf.asserts.assert_no_forwarding_loops(snapshot=SNAPSHOT)
    except BatfishAssertException as err:
        fail(str(err))
finally:
    bf.delete_network(NETWORK)

print("The selected checks passed for this snapshot.")

The runner needs to reach the Batfish service, and the service needs to run a version that goes with your pybatfish. It does not need to reach your devices. Where the candidate configurations come from, and how an approved change is deployed afterwards, is up to the rest of your tooling, which is why this section is not tied to one.

Batfish and Ansible

There is an Ansible integration for Batfish, but it is not current. Here is what the primary sources say, as checked on 2 October 2026:

  • The batfish/ansible repository describes itself as a snapshot of a Batfish Ansible integration from 2020. It is archived on GitHub, so it is read-only, and its last change was in December 2021.
  • Its README says Intentionet created the role so that users can embed Batfish, or Batfish Enterprise, pre-deployment validation into an Ansible playbook. The role is on Ansible Galaxy as batfish.base and includes modules such as bf_session, bf_init_snapshot, bf_extract_facts, bf_validate_facts and bf_assert.
  • No maintained official Ansible collection for Batfish was found when this guide was checked (2 October 2026). The Batfish and pybatfish documentation do not describe an Ansible integration.

So the plain answer to “does Batfish work with Ansible?” is that an archived role exists, it has had no maintenance for several years, and this guide has not tested it against current releases. If you want to use it, test it against the Batfish and pybatfish versions you run, and expect to maintain your own copy. No pybatfish-from-Ansible pattern is shown here because the Batfish documentation does not give one. The tool-neutral approach in the previous section, a pipeline step that runs pybatfish after your playbook has generated the configurations, does not depend on any Ansible module.

Supported platforms and limits

The supported devices page says Batfish supports configurations for a large and growing set of physical and virtual devices. As listed in the documentation and the README on the day this page was verified:

  • A10 Networks, Arista, Check Point, Fortinet and Palo Alto Networks.
  • Cisco: NX-OS, IOS, IOS-XE, IOS-XR and ASA devices.
  • Juniper: all Junos platforms, including MX, EX, QFX, SRX, T-series and PTX.
  • Cumulus, F5 BIG-IP, Free-Range Routing (FRR), Nokia (SR OS and MD-CLI) and SONiC, plus iptables on hosts. Cumulus, F5 BIG-IP and host files have their own packaging formats, described in the packaging guide.
  • Cloud: AWS constructs such as VPCs, network ACLs, gateways and security groups. Azure support is experimental: the v2026.08.27 release announced “Experimental Azure support”, and the formats documentation says Batfish reads Azure resource JSON views only, and that ARM templates are not supported.

Vendor support does not mean that every configuration feature on that platform is modelled. The documentation lists Aruba, Dell Force10 and Foundry as platforms with limited support.

Limits, as the project states them

The documentation does not publish a complete list of what Batfish does not model, and this guide does not invent one. What it does say is that Batfish may not fully support your configuration files, that it may ignore certain lines, and that configuration features that are not yet supported can be requested through Slack or a GitHub issue, or contributed as a pull request. The practical check is your own snapshot: run initIssues and fileParseStatus first, and read what was not recognised before you rely on an answer.

Two points from the README affect how far to trust a result. The core analysis uses only configuration files, optionally enriched with information such as external BGP routes or LLDP and CDP topology, so an answer is only as good as that input. And the results describe the configurations you gave it, not the live devices.

Where Batfish fits with configuration management

Batfish validates configuration snapshots. It takes configuration files as input and answers questions about them. A network can hold many named snapshots, and Batfish can compare them. What it does not do is collect configurations from devices or take scheduled backups of them, and the documentation does not describe those jobs as part of the tool.

That has a practical consequence. A snapshot is only as useful as the configurations in it, so you need a reliable source of the current, real configurations. Many teams take them from their backup or configuration management system, and the planned configurations from their templates or source of truth. Batfish then compares the two before anything is pushed.

rConfig sits on the collection side of that picture. It collects device configurations on a schedule, versions them, shows differences between versions, and checks them against compliance policy. See backing up network configurations and compliance and security auditing. The two are complementary: Batfish tells you what a configuration will do before it is deployed, and rConfig records and checks what is actually running. Neither replaces the other, and this guide does not describe a native integration between them. To see how automation tooling and a configuration platform divide the work, read network automation vs configuration management.

Troubleshooting common Batfish problems

These are the problems the project's documentation and the pybatfish source confirm. For anything else, the documentation and the project's Slack community are the places to ask.

Connection refused when you create a Session

Session(host=...) contacts the service on port 9996 while it loads the question list. If the service is not running, you get a connection error such as Connection refused from requests. Start the container, check that ports 9997 and 9996 are published, and check the host name. The server-side logs are available with docker logs batfish, assuming that you named the container batfish.

Parse warnings and unrecognised lines

Batfish may ignore lines it does not support. The documentation recommends running initIssues straight after you initialise a snapshot, and the getting-started notebook adds fileParseStatus, which shows each file's status, and parseWarning, which gives the detail. In the Batfish source a file's status is one of EMPTY, FAILED, IGNORED, ORPHANED, PARTIALLY_UNRECOGNIZED or PASSED, so anything other than PASSED is worth reading.

Check what Batfish could not parse
import os

from pybatfish.client.session import Session

bf = Session(host=os.environ.get("BATFISH_HOST", "localhost"))
bf.set_network("example_network")
bf.set_snapshot("current")

# Which files parsed completely, and which did not.
status = bf.q.fileParseStatus().answer().frame()
print(status[status["Status"] != "PASSED"])

# Lines Batfish did not recognise, and other warnings from conversion.
print(bf.q.initIssues().answer().frame())
print(bf.q.parseWarning().answer().frame())

The pandas filter in the documentation shows how to focus on what matters, for example by dropping rows about a warning you have decided to accept. If a construct you rely on is not supported, report it through GitHub.

Missing files in the snapshot layout

Batfish expects a folder with a configs subfolder holding the device configuration files, and an optional batfish subfolder for supplemental data. If you point init_snapshot at the wrong level, the snapshot will not contain your devices. Check the layout against the one shown earlier, and read the packaging guide for platforms such as Cumulus, F5 BIG-IP and hosts, which use their own formats. An empty or half-parsed snapshot shows up in fileParseStatus.

Version mismatch between the service and pybatfish

The documentation says to upgrade Batfish and pybatfish together, and warns that snapshots uploaded to an older Batfish can become incompatible with a newer one and need to be initialised again. The project's Docker repository also describes cross-version tests of new pybatfish releases against the most recent Batfish release, but the simplest rule is to run the latest image with the latest pybatfish, and to pin both in a pipeline. Check the installed client version with the command in the install example.

Memory on large snapshots

How much memory Batfish can use is set by your Docker configuration. The documented recommendation for your own network is at least 32 GB of RAM. Start by measuring what your snapshots need, give the container enough memory, and set a sensible --memory limit, as the Docker documentation advises. The Batfish README also mentions the --oom-kill-disable argument for Linux systems that run the out-of-memory killer. Docker warns against setting it, and says to use it only on containers where --memory is also set, because otherwise the host can run out of memory and the kernel may have to kill the host's own processes. Treat it as a last resort, not the normal fix. The documentation also notes that the first question that needs the data plane can take a few minutes on a large network, so a slow first answer is not necessarily a fault.

Using Batfish alongside rConfig

“Alongside” means the two can coexist. Batfish is a validation layer: it answers questions about configuration snapshots. rConfig is the collection and compliance layer: it gathers the running configurations, keeps their history and checks them. This guide does not describe a native integration between them.

What rConfig can cover, by edition

Not every edition includes every capability. This is how the current edition comparison on the rConfig site divides the capabilities discussed in this guide.

rConfig capabilities by edition, as listed in the edition comparison
EditionAdds
rConfig Core (free, open source)Scheduled backups, Compare configuration versions, Single sign-on
Starter and abovePolicy checks and compliance status, Role-based access control, RADIUS authentication, LDAP and Active Directory, User audit log, Restore a previous configuration, Push configuration changes
Standard and aboveCheck compliance continuously, Evidence you can hand to an auditor
Enterprise / MSPDistributed collectors, High availability, Manage every rConfig instance centrally

What stays with Batfish

rConfig does not claim to match Batfish for modelling network behaviour or validating a planned change. Asking whether a candidate configuration keeps traffic flowing, offline and before it is deployed, is what Batfish was built for. See the rConfig solution overview for what rConfig covers, and the integrations page for how it connects to other systems.

Choosing how to collect and manage configurations

If you already run Batfish, you do not have to choose. Teams that validate with Batfish still need current configurations to feed it, a record of what changed and when, and policy checks that evidence compliance. Whether to build those around your scripts or adopt a platform for part of the work depends on your requirements and the engineering capacity you can give them.

You may also be choosing among the libraries that sit near Batfish in a pipeline. The Netmiko guide, the Nornir guide and the Paramiko guide cover the tools that connect to devices and run tasks.

Evaluate rConfig Core alongside Batfish

rConfig V8 Core is free and open source, with no time limit and no device limit, so you can run it next to your Batfish workflow and see how it covers the collection side. If you want to talk through the paid editions, you can book a demo.

Batfish FAQ

What is Batfish in networking?

Batfish is an open source network configuration analysis tool that finds errors and checks the correctness of planned or current network configurations. It reads device configuration files, builds a vendor-neutral model of the network from them, and answers questions about that model, such as which flows can reach a destination, whether an ACL line can ever match, or whether a BGP session is configured to come up. It does this offline, from the files, without logging in to the devices.

Is Batfish free and open source?

Yes. Batfish and pybatfish are released under the Apache 2.0 licence (Apache-2.0), as shown on GitHub and PyPI. You can run the official Docker images yourself at no cost. Check the licence text in the repository for the exact terms before you redistribute anything.

Who maintains Batfish?

The pybatfish package on PyPI lists four maintainer accounts: arifogel, dhalperi, intentionet and ratul. batfish.org says the project was originally developed by researchers at Microsoft Research, UCLA and USC, was later enhanced and maintained by Intentionet, and that many others have contributed. The GitHub repository shows commits in the days before this page was verified, so the project is actively maintained.

What is pybatfish?

pybatfish is the Python client for the Batfish service. You create a Session, point it at a running service, initialise a snapshot from a folder of configuration files, and call questions such as bf.q.initIssues() or bf.q.routes(). Answers come back as objects that convert to pandas DataFrames with .frame(). The latest release is 2026.9.17.3748, released on 18 September 2026, and it needs Python 3.10 or newer.

How do I run Batfish?

Run the service from the official Docker images and install pybatfish with pip. The getting-started documentation recommends the batfish/allinone image, which bundles the service, pybatfish and example Jupyter notebooks. The batfish/batfish image contains the core service only. Both listen on ports 9997 and 9996, and the documentation recommends a server with at least a quad-core CPU and 32 GB of RAM when you move to your own network.

Does Batfish connect to my devices?

No. The Batfish documentation states that it does not require direct access to network devices. The core analysis needs only the configuration files. You can add information such as BGP routes received from external peers, or topology learned from LLDP or CDP, to enrich the analysis. Getting the configuration files off the devices is a separate job that you do with another tool.

Which vendors does Batfish support?

The documentation lists A10 Networks, Arista, AWS, Azure (experimental), Check Point, Cisco (IOS, IOS-XE, IOS-XR, NX-OS and ASA), Cumulus, F5 BIG-IP, Fortinet, Free-Range Routing, iptables on hosts, Juniper (all Junos platforms), Nokia, Palo Alto Networks and SONiC. It lists Aruba, Dell Force10 and Foundry as limited support. Support for a vendor does not mean that every configuration feature on that platform is modelled. Check the supported devices page for the current list, and run initIssues on your own configurations to see what is not recognised.

Can Batfish be used with Ansible?

Intentionet published an Ansible role for Batfish, available on Ansible Galaxy as batfish.base, with modules such as bf_session, bf_init_snapshot and bf_assert. Its GitHub repository is archived and read-only, with the last change in December 2021, and its description calls it a snapshot of an integration from 2020. No maintained official Ansible collection for Batfish was found when this guide was checked (2 October 2026). Treat the role as unmaintained, and test it yourself before you rely on it. The Batfish documentation does not describe an Ansible integration.

Can Batfish run in a CI pipeline?

Yes. The project describes pre-deployment validation as its primary use case. A pipeline step can build candidate configurations, initialise them as a snapshot, run questions or assertion helpers through pybatfish, and exit with a non-zero status when a check fails, so that the change does not go further. Batfish does not need access to the devices, so the step can run in an ordinary CI runner that can reach the Batfish service.

How does Batfish fit with configuration backup and compliance?

They do different jobs. Batfish validates configuration snapshots that you give it, and can keep several named snapshots and compare them. It does not collect configurations from devices or take scheduled backups of them. A backup and compliance system collects, stores, versions and compares the running configurations and checks them against policy. Many teams use the output of their backup system as the source of snapshots for Batfish, so the two are complementary and neither replaces the other.

Can I use Batfish alongside rConfig?

Yes, they can run side by side. Batfish validates configuration snapshots, and rConfig collects, versions and compares the running configurations, and checks them against policy. rConfig Core is free and open source and covers scheduled backups, comparison between configuration versions and single sign-on. Compliance policy checks, role-based access control, RADIUS sign-in and a user audit log are in the paid editions from Starter, and scheduled compliance checks and reports from Standard. This guide does not describe a native integration between rConfig and Batfish.

Further reading and credit

Batfish

On rConfig

Thank you to Ari Fogel, Stanley Fung, Luis Pedrosa, Meg Walraed-Sullivan, Ramesh Govindan, Ratul Mahajan and Todd Millstein, the authors of the original research, and to Matt Brown, Daniel Halperin and Victor Heorhiadi, who joined them in describing its evolution. Thank you to Intentionet, who enhanced and maintained Batfish, to the current maintainers, and to the many contributors who have built the project and keep it open source for the whole networking community.