FreeBSD.software
Home/Guides/Ansible on FreeBSD: Automation Review
review·2026-04-09·9 min read

Ansible on FreeBSD: Automation Review

A thorough review of Ansible on FreeBSD covering modules, playbooks, inventory management, roles, and comparisons with Salt and Puppet for FreeBSD system automation.

Ansible on FreeBSD: Automation Review

Configuration management on FreeBSD has historically been a mixed bag. While the operating system ships with excellent built-in tools like periodic scripts and rc.conf, scaling administration across dozens or hundreds of FreeBSD hosts demands something more structured. Ansible fills that gap without requiring an agent on managed nodes, which makes it particularly appealing for FreeBSD environments where minimizing third-party daemons is a common priority.

This review covers Ansible's FreeBSD integration as it stands today, from native modules and connection methods to practical playbook patterns, role design, and how it stacks up against Salt and Puppet for FreeBSD-centric shops.

Installation and Setup

Getting Ansible running on FreeBSD is straightforward. The control node (where you run playbooks from) can be FreeBSD itself or any system with Python. For a FreeBSD control node:

sh
pkg install py311-ansible

Or from ports if you need build-time options:

sh
cd /usr/ports/sysutils/ansible make install clean

Managed nodes only need Python and SSH access. FreeBSD ships with SSH out of the box, so the only prerequisite is Python:

sh
pkg install python311

Ansible detects the Python interpreter automatically on most FreeBSD systems, but you can pin it explicitly in your inventory or group variables:

sh
# In group_vars/freebsd.yml ansible_python_interpreter: /usr/local/bin/python3.11

One important note: Ansible uses /bin/sh as the default shell on FreeBSD, which is fine for most tasks. If you rely on bash-specific syntax in your shell modules, set ansible_shell_type: sh or install bash and point Ansible to it.

FreeBSD-Specific Modules

Ansible includes several modules purpose-built for FreeBSD, and this is where it earns real points compared to more Linux-centric tools.

Package Management with pkgng

The community.general.pkgng module handles FreeBSD's pkg system natively:

sh
# Install a package ansible freebsd_hosts -m community.general.pkgng -a "name=nginx state=present" # Install multiple packages ansible freebsd_hosts -m community.general.pkgng -a "name=nginx,redis,postgresql16-server state=present" # Update all packages ansible freebsd_hosts -m community.general.pkgng -a "name=* state=latest"

In playbook form:

sh
- name: Install web stack community.general.pkgng: name: - nginx - php83 - php83-extensions state: present cached: false

The cached parameter controls whether Ansible updates the package catalog before installing. Setting it to false ensures you always get the latest available versions.

Service Management

FreeBSD's rc system integrates cleanly with Ansible's service module. Ansible correctly uses /usr/sbin/service and manipulates /etc/rc.conf entries:

sh
- name: Enable and start nginx ansible.builtin.service: name: nginx state: started enabled: true

This writes nginx_enable="YES" to /etc/rc.conf and starts the service. Ansible handles the translation between its generic service abstraction and FreeBSD's rc framework automatically.

Sysrc Module

For direct rc.conf manipulation beyond services, the community.general.sysrc module is invaluable:

sh
- name: Set hostname community.general.sysrc: name: hostname value: "webserver01.example.com" - name: Configure static IP community.general.sysrc: name: ifconfig_em0 value: "inet 10.0.0.5 netmask 255.255.255.0"

This is cleaner than using lineinfile to edit rc.conf directly, because sysrc understands the file's semantics rather than treating it as plain text.

Jails

Ansible provides the community.general.iocage module for managing iocage jails:

sh
- name: Create a jail community.general.iocage: name: "webjail" state: started release: "14.1-RELEASE" properties: ip4_addr: "em0|10.0.0.100/24" host_hostname: "webjail" boot: "on"

For BastilleBSD or manual jail setups, you will likely fall back to command or shell modules with appropriate jail management commands, but iocage coverage is solid.

ZFS

ZFS management works through the community.general.zfs and community.general.zfs_facts modules:

sh
- name: Create dataset community.general.zfs: name: zroot/data/webfiles state: present extra_zfs_properties: compression: lz4 atime: off quota: 50G

Playbook Patterns for FreeBSD

A well-structured FreeBSD playbook typically separates concerns into roles. Here is a practical directory layout:

sh
site.yml inventory/ hosts.yml group_vars/ freebsd.yml webservers.yml dbservers.yml roles/ base/ tasks/main.yml handlers/main.yml templates/ nginx/ tasks/main.yml handlers/main.yml templates/nginx.conf.j2 pf/ tasks/main.yml templates/pf.conf.j2

A base role for all FreeBSD hosts might look like this:

sh
# roles/base/tasks/main.yml - name: Update package catalog community.general.pkgng: name: pkg state: present cached: false - name: Install base utilities community.general.pkgng: name: - sudo - vim - tmux - rsync state: present - name: Configure NTP community.general.sysrc: name: ntpd_enable value: "YES" - name: Start NTP ansible.builtin.service: name: ntpd state: started enabled: true - name: Deploy sshd config ansible.builtin.template: src: sshd_config.j2 dest: /etc/ssh/sshd_config owner: root group: wheel mode: "0644" notify: restart sshd

Handling PF Firewalls

PF configuration is a common task on FreeBSD. Ansible can template the ruleset and reload it safely:

sh
# roles/pf/tasks/main.yml - name: Deploy PF configuration ansible.builtin.template: src: pf.conf.j2 dest: /etc/pf.conf owner: root group: wheel mode: "0600" validate: "pfctl -nf %s" notify: reload pf

The validate parameter is critical here. It runs pfctl -nf on the templated file before deploying it, which catches syntax errors before they break your firewall.

Inventory Management

For FreeBSD environments, a YAML inventory works well:

sh
# inventory/hosts.yml all: children: freebsd: vars: ansible_python_interpreter: /usr/local/bin/python3.11 children: webservers: hosts: web01.example.com: web02.example.com: dbservers: hosts: db01.example.com: postgresql_version: "16" jailhosts: hosts: jail01.example.com: jails: - name: webjail ip: "10.0.0.100" - name: mailjail ip: "10.0.0.101"

For dynamic environments, you can write a custom inventory plugin that queries your infrastructure. FreeBSD jails are a particularly good candidate for dynamic inventory since jails can be created and destroyed frequently.

Roles and Galaxy

Ansible Galaxy has a growing number of FreeBSD-compatible roles. Search with:

sh
ansible-galaxy search --platforms FreeBSD nginx

However, community role quality varies significantly for FreeBSD. Many roles on Galaxy are Linux-only despite not declaring platform restrictions. Before adopting a Galaxy role, check:

  1. Does it use apt or yum hardcoded anywhere?
  2. Does it assume systemd?
  3. Does it reference Linux-specific paths like /etc/sysctl.d/?

Writing your own roles for FreeBSD-specific tasks is often more reliable than adapting Linux-centric Galaxy roles. The time spent writing a clean FreeBSD role pays off quickly in maintainability.

Performance and Connection Methods

Ansible connects to FreeBSD hosts over SSH by default. For large inventories, a few tweaks help:

sh
# ansible.cfg [ssh_connection] pipelining = True ssh_args = -o ControlMaster=auto -o ControlPersist=60s [defaults] forks = 20

Pipelining reduces the number of SSH operations per task. On FreeBSD, it works without issues as long as requiretty is not set in sudoers (it is not by default).

For local execution on the control node itself:

sh
ansible-playbook site.yml --connection=local

Ansible vs Salt on FreeBSD

Salt has decent FreeBSD support with native execution modules for pkg, service, and jail management. The key differences:

  • Architecture: Salt uses a master-minion model with a persistent agent. Ansible is agentless. On FreeBSD where you might want to minimize running daemons, Ansible's approach is appealing.
  • Speed: Salt is faster for large fleets because the minion is always connected. Ansible needs to establish SSH connections each run.
  • FreeBSD depth: Salt's FreeBSD modules are somewhat deeper in areas like jail management. Ansible's are broader but occasionally thinner.
  • Community: Ansible has a larger community overall, which means more examples and better Stack Overflow coverage, but Salt has dedicated FreeBSD contributors.

Ansible vs Puppet on FreeBSD

Puppet's FreeBSD support has declined in recent years. The key issues:

  • Agent requirement: Puppet requires an agent daemon on each managed node, plus a Puppet server. This is heavier than both Ansible and Salt.
  • FreeBSD modules: Puppet's core resource types cover pkg and service on FreeBSD, but advanced FreeBSD features like ZFS and jails require third-party modules that are inconsistently maintained.
  • Language: Puppet uses its own declarative DSL. Ansible uses YAML. For teams already writing shell scripts for FreeBSD administration, Ansible's imperative-with-declarative-syntax is often easier to adopt.
  • Ecosystem decline: Puppet's market share has been shrinking. Finding FreeBSD-specific Puppet modules and expertise is harder each year.

Limitations and Rough Edges

Ansible on FreeBSD is not without friction:

  • Module coverage gaps: Some Ansible modules assume Linux. The firewalld module is useless on FreeBSD; you need to manage PF through templates. The sysctl module works but behaves slightly differently than on Linux.
  • Fact gathering: ansible_facts populates correctly on FreeBSD for most fields, but some Linux-specific facts (like ansible_distribution_release) may be empty or unexpected.
  • Testing: Molecule (Ansible's testing framework) defaults to Docker containers, which do not run FreeBSD. Testing FreeBSD roles requires Vagrant with a FreeBSD box or a dedicated test host.
  • Python dependency: Every managed FreeBSD node needs Python. This is a minor annoyance on minimal installations where you might prefer to avoid it.

Verdict

Ansible is the strongest configuration management choice for FreeBSD environments today. Its agentless architecture aligns with FreeBSD's philosophy of running only what you need. The FreeBSD-specific modules for pkg, sysrc, ZFS, and iocage cover the most common administration tasks. Where gaps exist, Ansible's command and template modules provide a clean fallback.

For teams managing mixed FreeBSD and Linux fleets, Ansible's cross-platform abstraction is a genuine advantage. You can use the same playbook structure and inventory for both, with platform-specific roles handling the differences.

The main weakness is performance at scale. If you are managing hundreds of FreeBSD hosts and need sub-second response times, Salt's persistent agent model may serve you better. For most deployments, Ansible's SSH-based approach is fast enough and operationally simpler.

Rating: 8.5/10 -- Excellent FreeBSD integration with a mature ecosystem. Minor points lost for module coverage gaps and the inherent SSH overhead.

Frequently Asked Questions

Does Ansible work on FreeBSD without any modifications?

Yes. Ansible connects over SSH and requires only Python on the managed node. Both are available on FreeBSD by default or trivially installable. You may need to set ansible_python_interpreter if Python is not in the default path.

Can Ansible manage FreeBSD jails?

Yes. The community.general.iocage module manages iocage-based jails directly. For other jail managers like BastilleBSD, you can use command or shell modules. Ansible can also connect into jails directly using the jail connection plugin.

Is Ansible faster than Salt for FreeBSD automation?

Generally no. Salt's persistent agent model is faster for real-time operations on large fleets. Ansible's SSH-based approach adds connection overhead on each run. For most FreeBSD deployments under 100 hosts, the speed difference is negligible.

How do I test Ansible playbooks for FreeBSD?

Use Vagrant with a FreeBSD box for local testing. Molecule can be configured to use Vagrant instead of Docker. Alternatively, maintain a dedicated FreeBSD test host or VM that your CI pipeline can target.

Can Ansible manage ZFS on FreeBSD?

Yes. The community.general.zfs module creates and configures ZFS datasets, sets properties, and manages snapshots. The community.general.zfs_facts module gathers ZFS pool and dataset information for use in playbook logic.

Does Ansible handle FreeBSD upgrades?

Ansible can run freebsd-update commands through the command module, but there is no dedicated module for OS upgrades. For major version upgrades, a manual or semi-automated approach is still recommended due to the complexity involved.

Get more FreeBSD guides

Weekly tutorials, security advisories, and package updates. No spam.