203 Create DHCP jails with auto UUID and iocage_tags
Extending example 202 Create iocage templates. Clone DHCP jails..
Use case
Automatically generated UUID
Automatically generate the jails UUID names. At each iocage host, create three jails from the
template ansible_client
swarms:
sw_01:
count: 3
template: ansible_client
The module vbotka.freebsd.iocage doesn’t work with multiple names. Use
ansible.builtin.command instead. If the UUID is generated automatically, such a task is not
idempotent anyway. Example of the commands
iocage create --short --template ansible_client --count 3 bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_01 swarm=sw_01"
iocage start cd31c2a2 d254f889 158ef36d
The variable iocage_tags
In the inventory plugin, compose the variable iocage_tags
iocage_tags: dict(iocage_properties.notes | regex_findall('(\w+)=([\w\-]+)'))
For example,
iocage_tags:
vmm: iocage_01
swarm: sw_01
Create groups from iocage_tags
keyed_groups:
- prefix: swarm
key: iocage_tags.swarm
- prefix: vmm
key: iocage_tags.vmm
Tree
shell> tree .
.
├── ansible.cfg
├── group_vars
│ └── all
│ └── iocage.yml
├── hosts
│ ├── 02_iocage.yml
│ ├── 04_iocage.yml
│ └── 99_constructed.yml
├── iocage.ini
└── pb-test.yml
Synopsis
At two managed nodes:
iocage_02
iocage_04
In the playbook vbotka.freebsd.pb_iocage_ansible_clients.yml, use:
module vbotka.freebsd.iocage to:
create facts only
module
ansible.builtin.commandto:create jails
start jails
optionally, stop and destroy 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.
See also
Templates at iocage_02
[iocage_02]# iocage list -lt
+------+----------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
+======+================+======+=======+==========+=================+====================+=====+==========+==========+
| None | ansible_client | off | down | template | 14.3-RELEASE-p8 | DHCP (not running) | - | - | no |
+------+----------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+
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_02
iocage_04
[iocage:vars]
ansible_user=admin
ansible_become=true
ansible_python_interpreter=auto_silent
group_vars
properties:
bpf: 1
dhcp: 1
vnet: 1
notes: "vmm={{ inventory_hostname }}"
swarms:
sw_01:
count: 3
template: ansible_client
Playbook output - 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]
ok: [iocage_02]
TASK [Debug cmd_create debug=true] *********************************************
ok: [iocage_02] => (item=sw_01) =>
msg: |-
iocage create --short --template ansible_client --count 3 bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_02 swarm=sw_01"
ok: [iocage_04] => (item=sw_01) =>
msg: |-
iocage create --short --template ansible_client --count 3 bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_04 swarm=sw_01"
TASK [Create swarms] ***********************************************************
changed: [iocage_04] => (item=sw_01)
changed: [iocage_02] => (item=sw_01)
TASK [Debug create swarms debug=true] ******************************************
ok: [iocage_02] =>
out:
changed: true
msg: All items completed
results:
- ansible_loop_var: item
changed: true
cmd:
- iocage
- create
- --short
- --template
- ansible_client
- --count
- '3'
- bpf=1
- dhcp=1
- vnet=1
- notes=vmm=iocage_02 swarm=sw_01
delta: '0:00:04.581922'
end: '2026-02-24 23:26:23.563259'
failed: false
invocation:
module_args:
_raw_params: null
_uses_shell: false
argv: null
chdir: null
cmd: |-
iocage create --short --template ansible_client --count 3 bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_02 swarm=sw_01"
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
template: ansible_client
msg: ''
rc: 0
start: '2026-02-24 23:26:18.981337'
stderr: ''
stderr_lines: []
stdout: |-
a2508697 successfully created!
fd54d2f8 successfully created!
44dcc34f successfully created!
stdout_lines:
- a2508697 successfully created!
- fd54d2f8 successfully created!
- 44dcc34f successfully created!
skipped: false
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
- '3'
- bpf=1
- dhcp=1
- vnet=1
- notes=vmm=iocage_04 swarm=sw_01
delta: '0:00:00.853984'
end: '2026-02-24 23:26:20.579261'
failed: false
invocation:
module_args:
_raw_params: null
_uses_shell: false
argv: null
chdir: null
cmd: |-
iocage create --short --template ansible_client --count 3 bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_04 swarm=sw_01"
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
template: ansible_client
msg: ''
rc: 0
start: '2026-02-24 23:26:19.725277'
stderr: ''
stderr_lines: []
stdout: |-
16ecd72b successfully created!
c62c0b8a successfully created!
dbb0a0b0 successfully created!
stdout_lines:
- 16ecd72b successfully created!
- c62c0b8a successfully created!
- dbb0a0b0 successfully created!
skipped: false
TASK [Get iocage facts] ********************************************************
ok: [iocage_04]
ok: [iocage_02]
TASK [Debug cmd_start debug=true] **********************************************
ok: [iocage_04] => (item=sw_01) =>
msg: |-
iocage start 16ecd72b c62c0b8a dbb0a0b0
ok: [iocage_02] => (item=sw_01) =>
msg: |-
iocage start 44dcc34f fd54d2f8 a2508697
TASK [Start swarms] ************************************************************
changed: [iocage_04] => (item=sw_01)
changed: [iocage_02] => (item=sw_01)
TASK [Debug start swarms debug=true] *******************************************
ok: [iocage_02] =>
out:
changed: true
msg: All items completed
results:
- ansible_loop_var: item
changed: true
cmd:
- iocage
- start
- 44dcc34f
- fd54d2f8
- a2508697
delta: '0:00:38.471950'
end: '2026-02-24 23:27:24.707952'
failed: false
invocation:
module_args:
_raw_params: null
_uses_shell: false
argv: null
chdir: null
cmd: |-
iocage start 44dcc34f fd54d2f8 a2508697
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
template: ansible_client
msg: ''
rc: 0
start: '2026-02-24 23:26:46.236002'
stderr: |-
No default gateway found for ipv6.
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.
- No default gateway found for ipv6.
stdout: |-
* Starting 44dcc34f
+ Started OK
+ Using devfs_ruleset: 1000 (iocage generated default)
+ Configuring VNET OK
+ Using IP options: vnet
+ Starting services OK
+ Executing poststart OK
+ DHCP Address: 10.1.0.181/24
* Starting fd54d2f8
+ 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.141/24
* Starting a2508697
+ 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.241/24
stdout_lines:
- '* Starting 44dcc34f'
- ' + Started OK'
- ' + Using devfs_ruleset: 1000 (iocage generated default)'
- ' + Configuring VNET OK'
- ' + Using IP options: vnet'
- ' + Starting services OK'
- ' + Executing poststart OK'
- ' + DHCP Address: 10.1.0.181/24'
- '* Starting fd54d2f8'
- ' + 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.141/24'
- '* Starting a2508697'
- ' + 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.241/24'
skipped: false
ok: [iocage_04] =>
out:
changed: true
msg: All items completed
results:
- ansible_loop_var: item
changed: true
cmd:
- iocage
- start
- 16ecd72b
- c62c0b8a
- dbb0a0b0
delta: '0:00:24.510726'
end: '2026-02-24 23:27:11.477919'
failed: false
invocation:
module_args:
_raw_params: null
_uses_shell: false
argv: null
chdir: null
cmd: |-
iocage start 16ecd72b c62c0b8a dbb0a0b0
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
template: ansible_client
msg: ''
rc: 0
start: '2026-02-24 23:26:46.967193'
stderr: |-
No default gateway found for ipv6.
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.
- No default gateway found for ipv6.
stdout: |-
* Starting 16ecd72b
+ Started OK
+ Using devfs_ruleset: 1000 (iocage generated default)
+ Configuring VNET OK
+ Using IP options: vnet
+ Starting services OK
+ Executing poststart OK
+ DHCP Address: 10.1.0.109/24
* Starting c62c0b8a
+ 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.112/24
* Starting dbb0a0b0
+ 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.217/24
stdout_lines:
- '* Starting 16ecd72b'
- ' + Started OK'
- ' + Using devfs_ruleset: 1000 (iocage generated default)'
- ' + Configuring VNET OK'
- ' + Using IP options: vnet'
- ' + Starting services OK'
- ' + Executing poststart OK'
- ' + DHCP Address: 10.1.0.109/24'
- '* Starting c62c0b8a'
- ' + 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.112/24'
- '* Starting dbb0a0b0'
- ' + 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.217/24'
skipped: false
PLAY RECAP *********************************************************************
iocage_02 : ok=8 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
iocage_04 : ok=8 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
Jails at iocage_02
[iocage_02]# iocage list -l
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
+=====+==========+======+=======+======+=================+====================+=====+================+==========+
| 82 | 44dcc34f | off | up | jail | 14.3-RELEASE-p8 | epair0b|10.1.0.181 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 84 | a2508697 | off | up | jail | 14.3-RELEASE-p8 | epair0b|10.1.0.241 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 83 | fd54d2f8 | off | up | jail | 14.3-RELEASE-p8 | epair0b|10.1.0.141 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
Jails at iocage_04
[iocage_04]# iocage list -l
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
+=====+==========+======+=======+======+=================+====================+=====+================+==========+
| 61 | 16ecd72b | off | up | jail | 15.0-RELEASE-p3 | epair0b|10.1.0.109 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 62 | c62c0b8a | off | up | jail | 15.0-RELEASE-p3 | epair0b|10.1.0.112 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 63 | dbb0a0b0 | off | up | jail | 15.0-RELEASE-p3 | epair0b|10.1.0.217 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
Inventory hosts
plugin: vbotka.freebsd.iocage
host: 10.1.0.73
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\-]+)'))
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\-]+)'))
plugin: ansible.builtin.constructed
keyed_groups:
- prefix: swarm
key: iocage_tags.swarm
- prefix: vmm
key: iocage_tags.vmm
Note
The option get_properties: True is needed to get the dictionary iocage_properties
Display inventory
(env) > ansible-inventory -i hosts --graph
@all:
|--@ungrouped:
|--@swarm_sw_01:
| |--44dcc34f
| |--a2508697
| |--fd54d2f8
| |--16ecd72b
| |--c62c0b8a
| |--dbb0a0b0
|--@vmm_iocage_02:
| |--44dcc34f
| |--a2508697
| |--fd54d2f8
|--@vmm_iocage_04:
| |--16ecd72b
| |--c62c0b8a
| |--dbb0a0b0
Playbook pb-test.yml
- name: Connect to the group test.
hosts: swarm_sw_01
gather_facts: false
remote_user: admin
vars:
ansible_python_interpreter: auto_silent
tasks:
- ansible.builtin.command: hostname
register: out_host
- ansible.builtin.debug:
msg: >
ansible_host={{ ansible_host }}
iocage_tags={{ iocage_tags | to_yaml }}
Playbook output - Display iocage_tags
(env) > ansible-playbook pb-test.yml -i hosts
PLAY [Connect to the group test.] **********************************************
TASK [ansible.builtin.command] *************************************************
changed: [c62c0b8a]
changed: [16ecd72b]
changed: [dbb0a0b0]
changed: [fd54d2f8]
changed: [44dcc34f]
changed: [a2508697]
TASK [ansible.builtin.debug] ***************************************************
ok: [44dcc34f] =>
msg: |-
ansible_host=10.1.0.181 iocage_tags={swarm: sw_01, vmm: iocage_02}
ok: [a2508697] =>
msg: |-
ansible_host=10.1.0.241 iocage_tags={swarm: sw_01, vmm: iocage_02}
ok: [fd54d2f8] =>
msg: |-
ansible_host=10.1.0.141 iocage_tags={swarm: sw_01, vmm: iocage_02}
ok: [16ecd72b] =>
msg: |-
ansible_host=10.1.0.109 iocage_tags={swarm: sw_01, vmm: iocage_04}
ok: [c62c0b8a] =>
msg: |-
ansible_host=10.1.0.112 iocage_tags={swarm: sw_01, vmm: iocage_04}
ok: [dbb0a0b0] =>
msg: |-
ansible_host=10.1.0.217 iocage_tags={swarm: sw_01, vmm: iocage_04}
PLAY RECAP *********************************************************************
16ecd72b : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
44dcc34f : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
a2508697 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
c62c0b8a : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
dbb0a0b0 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
fd54d2f8 : 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