206 Create DHCP and fixed IP jails
Extending example 203 Create DHCP jails with auto UUID and iocage_tags.
Use case
In the inventory plugin vbotka.freebsd.iocage configuration file, use the option
hooks_results to get the DHCP IP address. This option is common for all jails in this example
hooks_results:
- /var/db/dhclient-hook.address.epair0b
It will silently fail in jails with fixed IP addresses. If the item fails, the result is the dash character ‘-’
iocage_hooks:
- '-'
This use case demonstrates the advantage of silently ignoring failed items over the potential
explicit error handling. Let the option compose pick what is needed
compose:
ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)
Fixed IP
One jail with fixed IP will be created from the template ansible_client in this example
clones:
test_131:
clone_from: ansible_client
properties:
ip4_addr: "em0|10.1.0.131/24"
notes: "swarm=sw_01"
Automatically generated UUID
Two DHCP jails with generated UUID will be created from the template ansible_client
swarms:
sw_01:
count: 3
template: ansible_client
properties:
bpf: 1
dhcp: 1
vnet: 1
Note
The clone test_131 belongs to the swarm sw_01. Set count: 3 to create two more jails
in the swarm sw_01.
The module vbotka.freebsd.iocage doesn’t work with multiple names. We will use
ansible.builtin.command instead. Anyway, such a task is not idempotent if the UUID is generated
automatically. Example of the commands
shell> iocage create --short --template ansible_client --count 2 bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_02 swarm=sw_01"
shell> iocage start cd31c2a2 d254f889
The variable iocage_tags
The inventory plugin composes the variable iocage_tags
iocage_tags: dict(iocage_properties.notes | regex_findall('(\w+)=([\w\-]+)'))
For example,
iocage_tags:
vmm: iocage_04
swarm: sw_01
This dictionary is used to create groups
keyed_groups:
- prefix: swarm
key: iocage_tags.swarm
- prefix: vmm
key: iocage_tags.vmm
Tree
shell> tree .
.
├── ansible.cfg
├── hosts
│ └── 04_iocage.yml
├── host_vars
│ └── iocage_04
│ └── iocage.yml
├── iocage.ini
└── pb-test.yml
Synopsis
At one managed node:
iocage_04
In the playbook vbotka.freebsd.pb_iocage_ansible_clients.yml, use:
module vbotka.freebsd.iocage to:
create one jail with fixed IP
start the jail
module
ansible.builtin.commandto:create two DHCP jails with generated UUID
start the jails
At all created jails:
In the playbook
pb-test.yml:connect to the created jails
display basic configuration of the jails.
Requirements
root privilege in the managed nodes
templates created in 202 Create iocage templates. Clone DHCP jails.
Notes
Templates created in 202 Create iocage templates. Clone DHCP jails. are used in this example.
The dash ‘-’ is used in binary iocage to represent a missing value. See for example:
inventory plugin vbotka.freebsd.iocage uses it too
if iocage_ip4_dict['ip4']: iocage_ip4 = ','.join([d['ip'] for d in iocage_ip4_dict['ip4']]) else: iocage_ip4 = '-'
See also
Templates at iocage_04
[iocage_04]# iocage list -lt
+------+-----------------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
+======+=======================+======+=======+==========+=================+====================+=====+==========+==========+
| None | ansible_client | off | down | template | 15.0-RELEASE-p3 | DHCP (not running) | - | - | no |
+------+-----------------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+
| None | ansible_client_apache | off | down | template | 15.0-RELEASE-p3 | DHCP (not running) | - | - | no |
+------+-----------------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+
| None | ansible_client_pull | off | down | template | 15.0-RELEASE-p3 | DHCP (not running) | - | - | no |
+------+-----------------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+
ansible.cfg
[defaults]
gathering = explicit
callback_result_format = yaml
display_skipped_hosts = false
host_key_checking = false
[connection]
pipelining = true
Inventory iocage.ini
iocage_02 ansible_host=10.1.0.73
iocage_04 ansible_host=10.1.0.29
[iocage]
iocage_04
[iocage:vars]
ansible_user=admin
ansible_become=true
ansible_python_interpreter=auto_silent
host_vars
properties:
notes: "vmm={{ inventory_hostname }}"
clones:
test_131:
clone_from: ansible_client
properties:
bpf: 1
vnet: 1
ip4_addr: 'vnet0|10.1.0.131/24'
notes: "swarm=sw_01"
swarms:
sw_01:
count: 3
template: ansible_client
properties:
bpf: 1
dhcp: 1
vnet: 1
start: [test_131]
Create and start clones
(env) > ansible-playbook vbotka.freebsd.pb_iocage_ansible_clients.yml -i iocage.ini \
-t clone -e clone=true
PLAY [Create and start jails. Optionally stop and destroy jails.] **************
TASK [Create clones from template] *********************************************
changed: [iocage_04] => (item=test_131 ansible_client)
TASK [Start clones] ************************************************************
changed: [iocage_04]
PLAY RECAP *********************************************************************
iocage_04 : ok=2 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
Create and start swarms
(env) > ansible-playbook vbotka.freebsd.pb_iocage_ansible_clients.yml -i iocage.ini \
-t swarm -e swarm=true -e debug=true
PLAY [Create and start jails. Optionally stop and destroy jails.] **************
TASK [Get iocage facts] ********************************************************
ok: [iocage_04]
TASK [Debug cmd_create debug=true] *********************************************
ok: [iocage_04] => (item=sw_01) =>
msg: |-
iocage create --short --template ansible_client --count 2 notes="vmm=iocage_04 swarm=sw_01" bpf=1 dhcp=1 vnet=1
TASK [Create swarms] ***********************************************************
changed: [iocage_04] => (item=sw_01)
TASK [Debug create swarms debug=true] ******************************************
ok: [iocage_04] =>
out:
changed: true
msg: All items completed
results:
- ansible_loop_var: item
changed: true
cmd:
- iocage
- create
- --short
- --template
- ansible_client
- --count
- '2'
- notes=vmm=iocage_04 swarm=sw_01
- bpf=1
- dhcp=1
- vnet=1
delta: '0:00:00.764700'
end: '2026-02-24 23:30:41.101203'
failed: false
invocation:
module_args:
_raw_params: null
_uses_shell: false
argv: null
chdir: null
cmd: |-
iocage create --short --template ansible_client --count 2 notes="vmm=iocage_04 swarm=sw_01" bpf=1 dhcp=1 vnet=1
creates: null
executable: null
expand_argument_vars: true
removes: null
stdin: null
stdin_add_newline: true
strip_empty_ends: true
item:
key: sw_01
value:
count: 3
properties:
bpf: 1
dhcp: 1
vnet: 1
template: ansible_client
msg: ''
rc: 0
start: '2026-02-24 23:30:40.336503'
stderr: ''
stderr_lines: []
stdout: |-
2f6cf965 successfully created!
e4c59701 successfully created!
stdout_lines:
- 2f6cf965 successfully created!
- e4c59701 successfully created!
skipped: false
TASK [Get iocage facts] ********************************************************
ok: [iocage_04]
TASK [Debug cmd_start debug=true] **********************************************
ok: [iocage_04] => (item=sw_01) =>
msg: |-
iocage start e4c59701 2f6cf965
TASK [Start swarms] ************************************************************
changed: [iocage_04] => (item=sw_01)
TASK [Debug start swarms debug=true] *******************************************
ok: [iocage_04] =>
out:
changed: true
msg: All items completed
results:
- ansible_loop_var: item
changed: true
cmd:
- iocage
- start
- e4c59701
- 2f6cf965
delta: '0:00:08.353942'
end: '2026-02-24 23:30:52.553598'
failed: false
invocation:
module_args:
_raw_params: null
_uses_shell: false
argv: null
chdir: null
cmd: |-
iocage start e4c59701 2f6cf965
creates: null
executable: null
expand_argument_vars: true
removes: null
stdin: null
stdin_add_newline: true
strip_empty_ends: true
item:
key: sw_01
value:
count: 3
properties:
bpf: 1
dhcp: 1
vnet: 1
template: ansible_client
msg: ''
rc: 0
start: '2026-02-24 23:30:44.199656'
stderr: |-
No default gateway found for ipv6.
No default gateway found for ipv6.
stderr_lines:
- No default gateway found for ipv6.
- No default gateway found for ipv6.
stdout: |-
* Starting e4c59701
+ Started OK
+ Using devfs_ruleset: 1001 (iocage generated default)
+ Configuring VNET OK
+ Using IP options: vnet
+ Starting services OK
+ Executing poststart OK
+ DHCP Address: 10.1.0.200/24
* Starting 2f6cf965
+ Started OK
+ Using devfs_ruleset: 1002 (iocage generated default)
+ Configuring VNET OK
+ Using IP options: vnet
+ Starting services OK
+ Executing poststart OK
+ DHCP Address: 10.1.0.228/24
stdout_lines:
- '* Starting e4c59701'
- ' + Started OK'
- ' + Using devfs_ruleset: 1001 (iocage generated default)'
- ' + Configuring VNET OK'
- ' + Using IP options: vnet'
- ' + Starting services OK'
- ' + Executing poststart OK'
- ' + DHCP Address: 10.1.0.200/24'
- '* Starting 2f6cf965'
- ' + Started OK'
- ' + Using devfs_ruleset: 1002 (iocage generated default)'
- ' + Configuring VNET OK'
- ' + Using IP options: vnet'
- ' + Starting services OK'
- ' + Executing poststart OK'
- ' + DHCP Address: 10.1.0.228/24'
skipped: false
PLAY RECAP *********************************************************************
iocage_04 : ok=8 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
Jails at iocage_04
[iocage_04]# iocage list -l
+-----+----------+------+-------+------+-----------------+---------------------+-----+----------------+----------+
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
+=====+==========+======+=======+======+=================+=====================+=====+================+==========+
| 69 | 2f6cf965 | off | up | jail | 15.0-RELEASE-p3 | epair0b|10.1.0.228 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+---------------------+-----+----------------+----------+
| 68 | e4c59701 | off | up | jail | 15.0-RELEASE-p3 | epair0b|10.1.0.200 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+---------------------+-----+----------------+----------+
| 67 | test_131 | off | up | jail | 15.0-RELEASE-p3 | vnet0|10.1.0.131/24 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+---------------------+-----+----------------+----------+
Inventory hosts
plugin: vbotka.freebsd.iocage
host: 10.1.0.29
user: admin
get_properties: True
hooks_results:
- /var/db/dhclient-hook.address.epair0b
compose:
ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)
iocage_tags: dict(iocage_properties.notes | regex_findall('(\w+)=([\w\-]+)'))
keyed_groups:
- prefix: swarm
key: iocage_tags.swarm
- prefix: vmm
key: iocage_tags.vmm
Note
The option get_properties: True is needed to compose the dictionary iocage_tags
Display inventory
(env) > ansible-inventory -i hosts --graph
@all:
|--@ungrouped:
|--@swarm_sw_01:
| |--2f6cf965
| |--e4c59701
| |--test_131
|--@vmm_iocage_04:
| |--2f6cf965
| |--e4c59701
| |--test_131
Playbook pb-test.yml
- hosts: swarm_sw_01
remote_user: admin
vars:
ansible_python_interpreter: auto_silent
tasks:
- ansible.builtin.command: hostname
register: out
- ansible.builtin.debug:
msg: |
out.stdout: {{ out.stdout }}
ansible_host: {{ ansible_host }}
iocage_hooks: {{ iocage_hooks }}
iocage_tags:
{{ iocage_tags | to_nice_yaml(2) | indent(2) }}
Playbook output - Display jails in the swarm
(env) > ansible-playbook pb-test.yml -i hosts
PLAY [swarm_sw_01] *************************************************************
TASK [ansible.builtin.command] *************************************************
changed: [2f6cf965]
changed: [test_131]
changed: [e4c59701]
TASK [ansible.builtin.debug] ***************************************************
ok: [2f6cf965] =>
msg: |-
out.stdout: 2f6cf965
ansible_host: 10.1.0.228
iocage_hooks: ['10.1.0.228']
iocage_tags:
swarm: sw_01
vmm: iocage_04
ok: [e4c59701] =>
msg: |-
out.stdout: e4c59701
ansible_host: 10.1.0.200
iocage_hooks: ['10.1.0.200']
iocage_tags:
swarm: sw_01
vmm: iocage_04
ok: [test_131] =>
msg: |-
out.stdout: test-131
ansible_host: 10.1.0.131
iocage_hooks: ['-']
iocage_tags:
swarm: sw_01
vmm: iocage_04
PLAY RECAP *********************************************************************
2f6cf965 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
e4c59701 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_131 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Hint
The below command stops and destroys the jails in swarms
ansible-playbook vbotka.freebsd.pb_iocage_ansible_clients.yml -i iocage.ini \
-t swarm_destroy -e swarm_destroy=true