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.

  1. A fresh FreeBSD 15.1 VM with a ZFS pool.
  2. jailops-priv init -pool zroot creates the user, datasets, directories and configuration, then the services start.
  3. A fleet from git. jailops-sync validate, plan, sync --apply create the networks, projects, jails and policies you declared.
  4. Stay in sync. drift tells you when the host differs from git; watch --once --apply pulls, plans and applies from cron.

The provisioning example wraps these steps in one script.

Switched IPv6 on a cloud server

examples/ipv6-switched-cloud

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.

shared/networks/shop.yaml, excerpt
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"
commands
# 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
expected plan, excerpt
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

examples/ipv6-routed-hetzner

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.

shared/networks/blog.yaml, excerpt
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"
commands
jailops-sync -C examples/ipv6-routed-hetzner validate
jailops-sync -C examples/ipv6-routed-hetzner plan
expected plan, excerpt
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

examples/web-stack

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.

projects/shop/backup-policies/nightly.yaml
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: extract
commands
jailops-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
expected plan, excerpt
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

examples/big-pool-tank

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.

hosts/nas1/storage/bulk.yaml
kind: Storage
metadata:
  name: bulk
spec:
  dataset: tank/bulk
  description: 16 TB HDD pool for bulk jail data
  compression: zstd
commands
# 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
expected plan, excerpt
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

examples/multi-host-fleet

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.

rollouts/web-upgrade.yaml
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}
commands
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
expected plan, excerpt
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 unmanaged

Fresh VM to running fleet

examples/provisioning

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.

fleet/projects/demo/jails/web.yaml
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]}
commands
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

examples/pkgops-packaged-service

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.

fleet/projects/apps/jails/hello.yaml, excerpt
  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]
commands
jailops-sync -C examples/pkgops-packaged-service/fleet validate
jailops-sync -C examples/pkgops-packaged-service/fleet plan
expected plan, excerpt
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

examples/minimal-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.

fleet.yaml
# 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
commands
jailops-sync -C examples/minimal-fleet validate
jailops-sync -C examples/minimal-fleet render --host web1
expected plan, excerpt
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.

Get started jailops-sync reference