ocifbsd is a FreeBSD-native OCI runtime by REVYTECH. This page is a hands-on cheat sheet: real, copy-pasteable commands and config for building images, wiring up VNET container networking, load balancing, live stats, and orchestration. Every example below is accurate — paste it as-is and adjust names and addresses to taste.
One thing to know up front: any command that prints JSON does so pretty-printed by default. Two global flags control this everywhere: -J/--pretty gives you indented, human-readable JSON (this is the default), and -c/--compact gives you single-line JSON for scripts and pipelines. The only output that stays compact on its own is streaming JSONL, where one object per line is the point.
Build an image from a Containerfile
Images are built from a Containerfile — if you have written a Dockerfile before, the syntax will feel familiar. Each instruction adds a layer: start from a base, install what you need, copy in your config, and declare the command to run. Here is a small one that produces an nginx image:
FROM local/freebsd:14
RUN pkg install -y nginx
COPY nginx.conf /usr/local/etc/nginx/nginx.conf
CMD ["nginx", "-g", "daemon off;"]
Build it and give it a tag. The trailing . is the build context — the directory ocifbsd reads the Containerfile and COPY sources from:
ocifbsd build --tag local/nginx:latest .
Create a pod network (VNET)
Containers get real, isolated networking through VNET — each one can have its own stack, addresses, and routes. Before attaching containers, create a pod network with a subnet and gateway. This sets up the bridge your containers will share:
ocifbsd network create podnet \
--subnet 10.88.0.0/24 \
--gateway 10.88.0.1
Give a container a VNET address
Order matters here. Create the container first, then attach it to the network and assign its address, and only then start it — the VNET settings need to be in place before the container’s stack comes up. This gives the container its own IP on the pod network, a gateway, the bridge to join, and a DNS resolver:
ocifbsd create --name nginx --image local/nginx:latest
ocifbsd network set nginx --vnet on --ip4 10.88.0.13/24 \
--gateway4 10.88.0.1 --bridge ocifbsdpodnet --dns 1.1.1.1
ocifbsd start nginx
Run the native layer-4 load balancer
ocifbsd ships its own layer-4 load balancer — no external proxy needed. Point it at a listen address and a set of backends, and it spreads connections across them. Here it fronts one container plus two other nodes:
ocifbsd proxy --listen 192.168.1.241:8080 \
--algo round-robin \
--backend 10.88.0.13:80 \
--backend 192.168.1.242:80 \
--backend 192.168.1.243:80 \
--workers 4
The --algo flag picks how connections are distributed: round-robin rotates evenly, random picks at random, least-conn favors the backend with the fewest active connections, and source-hash pins each client to the same backend (sticky sessions).
Live resource stats as JSON
Watch what your containers are actually doing with ocifbsd stats. By default it prints pretty, indented JSON so it is easy to read at a glance:
ocifbsd stats
A single container’s stats object looks like this — one key per line, two-space indented:
{
"id": "a1b2c3d4e5f6",
"name": "nginx",
"state": "running",
"memory_bytes": 20971520,
"threads": 4,
"cputime_sec": 12.47
}
For pipelines and log shippers, add --compact to emit one JSON object per line (JSONL) — compact and streaming-friendly, so tools like jq can read it record by record:
ocifbsd stats --compact
Orchestration primitives
Beyond single containers, ocifbsd has primitives for running things at scale. A service keeps a desired number of replicas running and can be scaled up or down; a pod groups containers that share a network; a stack brings up a whole multi-service app from one declaration:
ocifbsd service create web --image local/nginx:latest --replicas 3
ocifbsd service scale web --replicas 5
ocifbsd pod create frontend --network podnet
ocifbsd stack deploy --file stack.yaml
Open by default, restrict by example
ocifbsd ships permissive defaults on purpose: containers on the same pod network can talk to each other freely, so things just work while you are building. When you are ready to lock a container down, you opt in to the restriction rather than fighting the defaults. Here is the open default — two containers on podnet can reach any port on each other with no extra config.
To restrict a container to only the ports it actually needs, add a pf rule that permits those ports and blocks the rest. This example lets the nginx container accept HTTP on port 80 and drops everything else inbound to it:
# /etc/pf.conf — restrict the nginx container to port 80 only
nginx_ip = "10.88.0.13"
# allow HTTP in to the container
pass in quick proto tcp to $nginx_ip port 80 keep state
# block everything else headed to it
block in quick to $nginx_ip
Prefer to keep it inside ocifbsd? The same intent can be expressed as a network option when you attach the container, exposing only the ports you name and closing the rest:
ocifbsd network set nginx --vnet on --ip4 10.88.0.13/24 \
--gateway4 10.88.0.1 --bridge ocifbsdpodnet --dns 1.1.1.1 \
--restrict --expose 80/tcp
Start open, measure what a workload really needs, then narrow it down with an explicit rule you can read and review. That is the REVYTECH way: sensible open defaults, restriction as a deliberate, documented choice.
Deploy the whole cluster from one file (Ensemble)
The clustered deployment — routed pod networks, the synchronous MariaDB Galera cluster, the 3-master Redis Cluster, WordPress + Nginx on every node, and the native L4 proxy — is declared in a single Ensemble manifest. One command deploys it across all three nodes:
ocifbsd stack up -f cloudbsd-cluster.ensemble.yaml
Here is the full manifest — a cluster that can describe and rebuild itself:
# CloudBSD OCI cluster — Ensemble manifest
#
# Declarative description of the whole 3-node cluster: a routed pod network
# per node, a synchronous MariaDB Galera cluster, a 3-master Redis Cluster,
# the WordPress + Nginx web tier on every node, and the native ocifbsd L4
# proxy in front. `ocifbsd stack up -f cloudbsd-cluster.ensemble.yaml`
# deploys it across the nodes named below.
#
# This is the single file the cluster should have been born from.
apiVersion: ocifbsd/v1
kind: Stack
metadata:
name: cloudbsd
namespace: default
spec:
# The member nodes and the routed pod subnet each one owns. ocifbsd wires
# inter-node routes (no NAT between pods) so container IPs are directly
# reachable across nodes — which is what lets Galera and Redis Cluster form.
nodes:
- name: fb16-1
address: 192.168.1.241
podSubnet: 10.88.1.0/24
podGateway: 10.88.1.1
- name: fb16-2
address: 192.168.1.242
podSubnet: 10.88.2.0/24
podGateway: 10.88.2.1
- name: fb16-3
address: 192.168.1.243
podSubnet: 10.88.3.0/24
podGateway: 10.88.3.1
network:
bridge: ocifbsdpodnet
dns: 1.1.1.1
services:
# --- Synchronous data tier: MariaDB Galera, one instance per node ---
- kind: StatefulSet
name: mariadb
image: local/mariadb:latest
replicas: 3
placement: perNode # exactly one per node, on the node's pod net
ipHost: 11 # 10.88.<node>.11
ports: [3306, 4567, 4568, 4444]
galera:
clusterName: oci_galera
provider: /usr/local/lib/libgalera_smm.so
sstMethod: rsync
bootstrapNode: fb16-1 # bootstraps the primary component; peers SST/join
# --- Shared cache/session tier: 3-master Redis Cluster ---
- kind: StatefulSet
name: redis
image: local/redis:7
replicas: 3
placement: perNode
ipHost: 10 # 10.88.<node>.10
ports: [6379, 16379]
redisCluster:
masters: 3
announceIpFromPod: true
# --- Stateless web tier: WordPress (PHP-FPM) on every node ---
- kind: Deployment
name: wordpress
image: local/wordpress:latest
replicas: 3
placement: perNode
ipHost: 12 # 10.88.<node>.12
containerPort: 9000
env:
- DB_HOST=local # each node talks to its local Galera node
- REDIS=cluster # rediscluster session handler across seeds
# --- Stateless web tier: Nginx (web + FastCGI cache) on every node ---
- kind: Deployment
name: nginx
image: local/nginx:latest
replicas: 3
placement: perNode
ipHost: 13 # 10.88.<node>.13
ports: [80]
upstream: wordpress # fastcgi_pass to the local wordpress pod
# --- Native ocifbsd L4 load balancer in front of the web replicas ---
proxy:
node: fb16-1
listen: 192.168.1.241:8080
algo: round-robin
backends:
- 10.88.1.13:80
- 10.88.2.13:80
- 10.88.3.13:80
workers: perCore
circuitBreaker:
threshold: 3
cooldownSeconds: 10