Examples
Your whole fleet in git: eight you can copy
Each example is a complete repository of YAML in the source tree: validate it offline, read the plan, apply it. The plans below are what jailops-sync plan prints for a host that has nothing yet, and the test suite compares them with the real output on every run.
From a fresh VM to a fleet, in minutes
The provisioning story.
- A fresh FreeBSD 15.1 VM with a ZFS pool.
jailops-priv init -pool zrootcreates the user, datasets, directories and configuration, then the services start.- A fleet from git.
jailops-sync validate,plan,sync --applycreate the networks, projects, jails and policies you declared. - Stay in sync.
drifttells you when the host differs from git;watch --once --applypulls, plans and applies from cron.
The provisioning example wraps these steps in one script.
Switched IPv6 on a cloud server
A Netcup-style server with a shared /64: proxy mode, a /80 per project, two projects kept apart, a firewall policy per jail and one public web jail with its own address.
kind: Network
metadata:
name: shop
spec:
uplink: vtnet0
ipv4: {subnet: 10.94.0.0/24, gateway: 10.94.0.1, range: 10.94.0.2-10.94.0.254, nat: true}
ipv6:
mode: proxy
subnet: "2001:db8:1:2:100::/80"
gateway: "fe80::1"
range: "2001:db8:1:2:100::10-2001:db8:1:2:100::ffff"# the operator approves the proxy once, in the root-owned priv.conf ndp-proxy = vtnet0=2001:db8:1:2:100::/79 jailops-sync -C examples/ipv6-switched-cloud validate jailops-sync -C examples/ipv6-switched-cloud plan jailops-sync -C examples/ipv6-switched-cloud sync --apply
host cloud1 create Image/freebsd-15 create Network/intra create Network/shop create Project/intra create Project/shop ... create Jail/intra/wiki create Jail/shop/app create Jail/shop/web 16 create, 0 update, 0 delete, 0 unchanged, 0 unmanaged
Routed IPv6 on a dedicated server
A Hetzner-style server whose /64 is routed to it: routed mode, no proxy, a /80 per project (a routed /64 holds 65,536 of them), projects blog and mail.
kind: Network
metadata:
name: blog
spec:
uplink: vtnet0
ipv6:
mode: routed
subnet: "2001:db8:abcd:1:1::/80"
gateway: "2001:db8:abcd:1:1::1"
range: "2001:db8:abcd:1:1::10-2001:db8:abcd:1:1::ffff"jailops-sync -C examples/ipv6-routed-hetzner validate jailops-sync -C examples/ipv6-routed-hetzner plan
host hz1 create Image/freebsd-15 create Network/blog create Network/mail ... 11 create, 0 update, 0 delete, 0 unchanged, 0 unmanaged
Web stack with encrypted backups
nginx, an application and PostgreSQL in one project, a volume for the database, a SOPS secret, and a nightly backup policy to a keyless backup node: encrypted on the host, retention applied on the node, a weekly restore test.
kind: BackupPolicy
metadata:
name: nightly
spec:
selector: {backup: nightly}
targets: [vault]
schedule: "17 2 * * *"
retention: {daily: 14, weekly: 8, monthly: 12}
verify_schedule: "30 4 * * 0"
verify_mode: extractjailops-sync -C examples/web-stack validate jailops-sync -C examples/web-stack plan jailops-sync -C examples/web-stack secrets check jailops-sync -C examples/web-stack sync --apply
host app1 create Image/freebsd-15 create Network/shop create Project/shop ... create BackupTarget/vault create BackupPolicy/shop/nightly create Jail/shop/app create Jail/shop/nginx create Jail/shop/postgres 14 create, 0 update, 0 delete, 0 unchanged, 0 unmanaged
A 16 TB pool for bulk data
A spinning-disk pool registered as a storage of its own, with a project quota, volumes, and an existing archive dataset mounted read-only into a jail. The archive is not backed up by JailOps, and host mounts need a global-admin token and an admin-host-mount line in priv.conf.
kind: Storage metadata: name: bulk spec: dataset: tank/bulk description: 16 TB HDD pool for bulk jail data compression: zstd
# priv.conf: admin-host-mount = /tank/archive jailops-sync -C examples/big-pool-tank validate jailops-sync -C examples/big-pool-tank plan jailops-sync -C examples/big-pool-tank sync --apply
host nas1 create Image/freebsd-15 create Network/media create Storage/bulk create Project/media ... 9 create, 0 update, 0 delete, 0 unchanged, 0 unmanaged
One project across several hosts
Two hosts, one project defined once, a group, SOPS-sealed tokens and an mTLS remote file, and a rollout with a canary, batches and a health gate.
kind: Rollout
metadata:
name: web-upgrade
spec:
project: site
selector: {tier: web}
canary: 1
batch_size: 1
pause: 30s
health_gate: {timeout: 2m, interval: 5s}jailops-sync -C examples/multi-host-fleet validate jailops-sync -C examples/multi-host-fleet secrets check jailops-sync -C examples/multi-host-fleet plan --host @prod jailops-sync -C examples/multi-host-fleet rollout run web-upgrade
host eu1
create Image/freebsd-15
create Network/web
create Project/site
...
create Jail/site/web-eu1
host eu2
...
create Jail/site/web-eu2
projects
site
eu1 will be created
eu2 will be created
12 create, 0 update, 0 delete, 0 unchanged, 0 unmanagedFresh VM to running fleet
A fresh FreeBSD 15.1 or newer VM with a ZFS pool becomes a JailOps host and applies a fleet from git, in one repeatable script: packages, pf anchors, forwarding, jailops-priv init -pool, the services, a jailops doctor gate, then jailops-sync validate, plan and sync --apply. The fleet reaches the host through the local socket, so no token or certificate is stored in the repository.
kind: Jail
hosts: [self]
start: true
metadata:
name: web
spec:
image: freebsd-15@stable
storage: default
network: [{name: eth0, network: lan, ipv4: auto}]
firewall: {policy: web}
resources: {memory: 512M}
proxy: [{name: http, listen: ":80", jail_port: 80}]
provision:
packages: [nginx]
services: {enable: [nginx], start: [nginx]}JAILOPS_POOL=zroot \ JAILOPS_PKG_DIR=/root/pkgs \ JAILOPS_FLEET_REPO=https://git.example.org/ops/fleet.git \ sh bootstrap.sh # afterwards jailops-sync -C /var/db/jailops-fleet drift jailops-sync -C /var/db/jailops-fleet watch --once --apply
The pkg.jailops.net repository is not live yet, so the packages come from JAILOPS_PKG_DIR or a repository you configure. A user-data script for cloud images is included; deliver the age key out of band, never in user-data.
Your own Go service, packaged with PkgOps
A Go service built and signed with PkgOps, installed into a jail declared in the fleet through its provision commands. Today the two repository files go in the project-files directory on the host by hand; shipping them with the fleet and a first-class package source are planned, not built.
provision:
files:
- {source: pkg/hello.conf, destination: /usr/local/etc/pkg/repos/hello.conf, mode: "0644"}
- {source: pkg/hello.pub, destination: /usr/local/etc/pkg/keys/hello.pub, mode: "0644"}
commands:
- [pkg, update, -f]
- [pkg, install, -y, hello]
- [sysrc, hello_enable=YES]
- [service, hello, start]jailops-sync -C examples/pkgops-packaged-service/fleet validate jailops-sync -C examples/pkgops-packaged-service/fleet plan
host self create Image/freebsd-15 create Network/apps create Project/apps create FirewallPolicy/apps/floor create FirewallPolicy/apps/hello create Jail/apps/hello 6 create, 0 update, 0 delete, 0 unchanged, 0 unmanaged
The smallest complete fleet
Two hosts, projects web and db, profiles, a firewall policy per project, SOPS and age secrets, mTLS to one host and a rollout. Start here to see what every file does.
# The fleet id becomes the ownership marker "managed_by: sync:minimal" on every # object jailops-sync creates. Only objects with that marker are ever changed # or deleted. id: minimal
jailops-sync -C examples/minimal-fleet validate jailops-sync -C examples/minimal-fleet render --host web1
host web1 create Image/freebsd-15 create Network/br-db create Network/br-web ... host web2 ... 21 create, 0 update, 0 delete, 0 unchanged, 0 unmanaged
Verified and next
Verified: every example validates offline, every object is accepted by the daemon's own validators, and each plan is compared with the real output by the test suite, against in-memory hosts. The individual steps, for example networks and jails created by the CLI, ran on FreeBSD 15.1. Not yet: these examples applied to a real host, build: true images through sync, host mounts through sync, bootstrap.sh on a fresh VM, user-data through nuageinit, and pkg install inside a jail. Credentials, backup keys and the operator's priv.conf are never declared in a fleet, by design.