FreeBSD.software
Home/Guides/FreeBSD in Modern DevOps: Complete Guide
guide·2026-04-09·10 min read

FreeBSD in Modern DevOps: Complete Guide

FreeBSD for DevOps: using jails as containers, Ansible automation, CI/CD pipelines, Terraform provisioning, cloud deployment, and monitoring in production environments.

FreeBSD in Modern DevOps: Complete Guide

FreeBSD is not the first operating system most DevOps engineers think of. The container ecosystem assumes Linux. Terraform providers default to Ubuntu. CI/CD tutorials start with Docker. But FreeBSD has its own answers to every DevOps requirement -- and in several areas, those answers are technically superior.

Jails predate Docker by over a decade and provide stronger isolation. ZFS gives you atomic snapshots and replication without third-party tools. The ports system builds reproducible packages. The base system ships a complete, auditable operating system. The rc system manages services without the complexity of systemd.

This guide covers how to run a modern DevOps workflow on FreeBSD -- from infrastructure provisioning through deployment, configuration management, CI/CD, and monitoring.

Jails as Containers

FreeBSD jails are the original container technology. They provide process isolation, filesystem isolation, and network isolation at the kernel level. Unlike Docker containers on Linux (which layer namespaces, cgroups, seccomp, and AppArmor to approximate isolation), jails are a single, coherent kernel feature.

Creating Jails for Application Deployment

Using Bastille for jail management:

sh
pkg install bastille sysrc bastille_enable="YES" bastille bootstrap 14.2-RELEASE update

Create application jails:

sh
bastille create api-server 14.2-RELEASE 10.0.0.10 bastille create db-server 14.2-RELEASE 10.0.0.11 bastille create cache-server 14.2-RELEASE 10.0.0.12

Install application stacks via templates:

sh
bastille template api-server myorg/python-api bastille template db-server myorg/postgresql bastille template cache-server myorg/redis

Jail Templates as Infrastructure Code

A Bastille template for a Python API server:

shell
PKG python311 py311-pip py311-gunicorn nginx SYSRC nginx_enable=YES CP usr/local/etc/nginx/nginx.conf CMD pip install -r /app/requirements.txt FSTAB /data/api-server/app app nullfs rw 0 0 SERVICE nginx start

Store templates in Git. Version control your infrastructure. Deploy identically across development, staging, and production.

Thin Jails for Density

Thin jails share the base system via nullfs mounts, saving disk space:

sh
bastille create --thin app1 14.2-RELEASE 10.0.0.20 bastille create --thin app2 14.2-RELEASE 10.0.0.21 bastille create --thin app3 14.2-RELEASE 10.0.0.22

Each thin jail uses only the disk space for its unique files. A server with 50 thin jails uses only marginally more disk than one thick jail. Updates to the base release propagate to all thin jails automatically.

Ansible on FreeBSD

Ansible is the most FreeBSD-compatible configuration management tool. It connects via SSH, requires only Python on the target, and has built-in FreeBSD modules.

Setting Up Ansible

On your control machine:

sh
pkg install py311-ansible

Create an inventory for your FreeBSD fleet:

sh
cat > /usr/local/etc/ansible/hosts << 'EOF' [webservers] web1.example.com web2.example.com [databases] db1.example.com [freebsd:children] webservers databases [freebsd:vars] ansible_python_interpreter=/usr/local/bin/python3.11 ansible_become_method=su EOF

FreeBSD-Specific Ansible Modules

Ansible includes modules designed for FreeBSD:

yaml
# Install packages with pkgng - name: Install web stack community.general.pkgng: name: - nginx - postgresql16-server - redis state: present # Manage rc.conf settings - name: Enable services community.general.sysrc: name: "{{ item }}_enable" value: "YES" loop: - nginx - postgresql - redis # Manage services - name: Start services ansible.builtin.service: name: "{{ item }}" state: started loop: - nginx - postgresql - redis # Manage PF firewall - name: Deploy PF configuration ansible.builtin.copy: src: pf.conf dest: /etc/pf.conf owner: root mode: '0600' notify: reload pf # Manage jails - name: Create application jail ansible.builtin.command: cmd: bastille create {{ jail_name }} 14.2-RELEASE {{ jail_ip }} creates: /usr/local/bastille/jails/{{ jail_name }}

Ansible Playbook for FreeBSD Server Setup

A complete playbook for provisioning a FreeBSD web server:

sh
cat > /usr/local/etc/ansible/playbooks/webserver.yml << 'YAML' --- - name: Configure FreeBSD web server hosts: webservers become: true vars: domain: example.com app_user: www pkg_list: - nginx - python311 - py311-gunicorn - git-lite tasks: - name: Update package repository community.general.pkgng: name: pkg state: latest - name: Install packages community.general.pkgng: name: "{{ pkg_list }}" state: present - name: Enable nginx community.general.sysrc: name: nginx_enable value: "YES" - name: Deploy nginx configuration ansible.builtin.template: src: templates/nginx.conf.j2 dest: /usr/local/etc/nginx/nginx.conf notify: reload nginx - name: Deploy PF firewall rules ansible.builtin.template: src: templates/pf.conf.j2 dest: /etc/pf.conf notify: reload pf - name: Start nginx ansible.builtin.service: name: nginx state: started handlers: - name: reload nginx ansible.builtin.service: name: nginx state: reloaded - name: reload pf ansible.builtin.command: cmd: pfctl -f /etc/pf.conf YAML

Run it:

sh
ansible-playbook /usr/local/etc/ansible/playbooks/webserver.yml

CI/CD on FreeBSD

Buildbot

Buildbot is written in Python, runs natively on FreeBSD, and is more flexible than Jenkins for FreeBSD workloads:

sh
pkg install py311-buildbot py311-buildbot-www py311-buildbot-worker

Configure a FreeBSD build worker:

sh
buildbot-worker create-worker /var/buildbot-worker \ buildmaster.example.com:9989 freebsd-worker pass123 sysrc buildbot_worker_enable="YES" service buildbot-worker start

Concourse CI

Concourse supports FreeBSD workers. Install in a jail:

sh
bastille create ci-server 14.2-RELEASE 10.0.0.50 bastille pkg ci-server install concourse

GitLab Runner

GitLab Runner works on FreeBSD for shell executors:

sh
pkg install gitlab-runner sysrc gitlab_runner_enable="YES" gitlab-runner register \ --url https://gitlab.example.com/ \ --token YOUR_TOKEN \ --executor shell \ --shell bash service gitlab-runner start

The shell executor runs CI jobs directly on FreeBSD. For isolation, run each GitLab Runner inside its own jail:

sh
bastille create runner1 14.2-RELEASE 10.0.0.60 bastille pkg runner1 install gitlab-runner git-lite bastille cmd runner1 gitlab-runner register \ --url https://gitlab.example.com/ \ --token YOUR_TOKEN \ --executor shell

CI/CD Pipeline for FreeBSD Projects

A .gitlab-ci.yml for a FreeBSD project:

yaml
stages: - build - test - deploy build: stage: build tags: - freebsd script: - make clean - make -j$(sysctl -n hw.ncpu) - make package test: stage: test tags: - freebsd script: - make test - kyua test - kyua report-html --output=test-results/ deploy: stage: deploy tags: - freebsd only: - main script: - bastille cmd production-app "service myapp stop" - cp build/myapp /usr/local/bastille/jails/production-app/root/usr/local/bin/ - bastille cmd production-app "service myapp start"

Terraform and FreeBSD

Terraform provisions infrastructure. FreeBSD support depends on your cloud provider.

DigitalOcean

DigitalOcean has native FreeBSD droplet support:

sh
pkg install terraform
shell
# main.tf terraform { required_providers { digitalocean = { source = "digitalocean/digitalocean" version = "~> 2.0" } } } provider "digitalocean" { token = var.do_token } resource "digitalocean_droplet" "web" { count = 3 name = "web-${count.index + 1}" region = "nyc3" size = "s-2vcpu-4gb" image = "freebsd-14-2-x64" ssh_keys = [var.ssh_fingerprint] connection { type = "ssh" user = "freebsd" host = self.ipv4_address } provisioner "remote-exec" { inline = [ "sudo pkg install -y nginx", "sudo sysrc nginx_enable=YES", "sudo service nginx start" ] } } output "web_ips" { value = digitalocean_droplet.web[*].ipv4_address }

Apply:

sh
terraform init terraform plan terraform apply

Vultr

Vultr also supports FreeBSD instances natively:

shell
resource "vultr_instance" "freebsd" { plan = "vc2-2c-4gb" region = "ewr" os_id = 401 # FreeBSD 14 hostname = "freebsd-server" ssh_key_ids = [vultr_ssh_key.mykey.id] }

Hetzner Cloud

shell
resource "hcloud_server" "freebsd" { name = "freebsd-web" server_type = "cx22" image = "freebsd-14.2" location = "fsn1" ssh_keys = ["my-key"] }

Cloud Deployment Patterns

Immutable Infrastructure with ZFS

FreeBSD's ZFS enables immutable deployment patterns without Docker:

sh
# Create a golden image bastille create golden-web 14.2-RELEASE 10.0.0.100 bastille template golden-web myorg/web-stack bastille stop golden-web # Snapshot the golden image zfs snapshot zroot/bastille/jails/golden-web@v1.2.3 # Deploy by cloning zfs clone zroot/bastille/jails/golden-web@v1.2.3 zroot/bastille/jails/web-prod-1 zfs clone zroot/bastille/jails/golden-web@v1.2.3 zroot/bastille/jails/web-prod-2 # Roll back a bad deployment zfs rollback zroot/bastille/jails/web-prod-1@v1.2.2

Blue-Green Deployments

sh
# Blue is currently active (10.0.0.10) # Deploy green bastille create green-web 14.2-RELEASE 10.0.0.11 bastille template green-web myorg/web-stack-v2 # Test green curl http://10.0.0.11/health # Switch traffic (update PF or load balancer) pfctl -t webservers -T replace 10.0.0.11 # Destroy old blue if green is healthy bastille destroy blue-web bastille rename green-web blue-web

Monitoring

Prometheus and Grafana

sh
pkg install prometheus grafana sysrc prometheus_enable="YES" sysrc grafana_enable="YES"

Configure Prometheus to scrape FreeBSD targets:

sh
cat > /usr/local/etc/prometheus.yml << 'EOF' global: scrape_interval: 15s scrape_configs: - job_name: 'node' static_configs: - targets: - 'web1.example.com:9100' - 'web2.example.com:9100' - 'db1.example.com:9100' EOF

Install node_exporter on each FreeBSD host:

sh
pkg install node_exporter sysrc node_exporter_enable="YES" service node_exporter start

FreeBSD-Specific Monitoring

Monitor jails with custom metrics:

sh
# Count running jails jls | tail -n +2 | wc -l # Per-jail resource usage with rctl rctl -u jail:webserver # ZFS pool health zpool status -x zpool list -o name,size,alloc,free,fragmentation,capacity,health

Netdata

Netdata provides real-time monitoring with zero configuration:

sh
pkg install netdata sysrc netdata_enable="YES" service netdata start

Access the dashboard at http://your-server:19999. Netdata auto-detects FreeBSD services, ZFS pools, network interfaces, and jail resource usage.

Secrets Management

Vault on FreeBSD

HashiCorp Vault runs on FreeBSD:

sh
pkg install vault sysrc vault_enable="YES"

Configure Vault for production:

sh
cat > /usr/local/etc/vault/vault.hcl << 'EOF' storage "file" { path = "/var/db/vault/data" } listener "tcp" { address = "0.0.0.0:8200" tls_cert_file = "/usr/local/etc/ssl/vault.crt" tls_key_file = "/usr/local/etc/ssl/vault.key" } api_addr = "https://vault.example.com:8200" EOF service vault start vault operator init

Package Building and Distribution

Poudriere for Custom Packages

Poudriere builds custom FreeBSD packages in clean jail environments:

sh
pkg install poudriere

Create a build environment:

sh
poudriere jail -c -j 142amd64 -v 14.2-RELEASE poudriere ports -c -p default

Create a package list:

sh
cat > /usr/local/etc/poudriere.d/pkglist << 'EOF' www/nginx databases/postgresql16-server databases/redis lang/python311 sysutils/bastille security/vault EOF

Build:

sh
poudriere bulk -j 142amd64 -p default -f /usr/local/etc/poudriere.d/pkglist

Poudriere produces a complete pkg repository that you can serve via nginx and point your FreeBSD servers at. This gives you reproducible package builds with custom options -- the FreeBSD equivalent of building custom Docker images.

GitOps Workflow

A complete GitOps workflow on FreeBSD:

  1. Infrastructure definitions (Terraform) in Git
  2. Server configurations (Ansible playbooks) in Git
  3. Jail templates (Bastille templates) in Git
  4. Application code in Git
  5. CI/CD (GitLab Runner on FreeBSD) builds, tests, and deploys
sh
# Developer pushes code git push origin main # GitLab CI runs on FreeBSD runner # -> Builds application # -> Runs tests in a jail # -> Deploys to production jail # Infrastructure changes terraform apply # Provisions/modifies FreeBSD servers ansible-playbook site.yml # Configures servers bastille template production-app myorg/app # Deploys app

This is the same workflow that Linux/Docker shops use, adapted for FreeBSD's native tooling. The key difference: jails replace containers, ZFS replaces volume drivers, Bastille templates replace Dockerfiles, and Poudriere replaces Docker Hub.

FAQ

Can FreeBSD replace Linux in a DevOps workflow?

For most server workloads, yes. The tools exist: Ansible, Terraform, Prometheus, Grafana, GitLab Runner, Vault, and more all run on FreeBSD. The main gaps are Docker (not available on FreeBSD) and Kubernetes (which requires Linux). If your workflow is Docker/K8s-centric, FreeBSD requires rethinking. If your workflow is VM or bare-metal-centric, FreeBSD fits naturally.

Is Ansible the best config management tool for FreeBSD?

Ansible has the best FreeBSD support among popular config management tools. Puppet and Chef work but have smaller FreeBSD communities. Salt works but its FreeBSD modules are less complete. Ansible's agentless architecture (just SSH + Python) makes it the simplest to deploy on FreeBSD.

How do I run Docker on FreeBSD?

You do not. Docker requires the Linux kernel. On FreeBSD, use jails for container-like isolation. If you absolutely need Docker, run it inside a Linux VM on bhyve. But in most cases, jails provide better isolation and performance than Docker containers.

Can Terraform provision FreeBSD servers on AWS?

AWS does not offer official FreeBSD AMIs, but the FreeBSD project publishes community AMIs for EC2. You can reference these in Terraform. However, DigitalOcean, Vultr, and Hetzner have better first-party FreeBSD support and are easier to use with Terraform.

How do I handle secrets in FreeBSD jails?

Use Vault for centralized secrets management. Applications in jails authenticate to Vault using AppRole or token-based auth. For simpler setups, use Ansible Vault to encrypt secrets in your playbooks and deploy them to jails during provisioning.

Is there a FreeBSD equivalent of Kubernetes?

Not directly. The closest pattern is using a jail orchestrator (Bastille, CBSD) with a load balancer (HAProxy, relayd) and ZFS for storage. For service discovery, use DNS. This lacks Kubernetes' automatic scheduling and self-healing but covers the deployment and scaling needs of most workloads. Nomad by HashiCorp runs on FreeBSD and provides some orchestration capabilities.

Get more FreeBSD guides

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