List all jails. Example:
jlist +--------------+-----------+--------------------+---------------------------------------------------------------+ | runs/stopped | version | autostart ip/ip | note | +--------------+-----------+--------------------+---------------------------------------------------------------+ | astZf1 | 14.3-R | 81.xx.xx.52/27 | Asterisk worker | | dbTest | 14.2-R | 192.168.144.157/24 | | | dbZFS | 14.2-R | 192.168.144.154/24 | Starší mysql s innodb, po 5 vteřinách přerušuje dlouhé dotazy | | dbZFSino | 14.2-R-p3 | 192.168.144.156/24 | Nejnovější mariadb s innoDB | | dbZFSsata | 14.2-R | 192.168.144.153/24 | | | pkg | 14.3-R | 192.168.144.152/24 | Created 2025-06-17 11:14:12 | | ucet | 14.3-R | 192.168.144.155/24 | | +--------------+-----------+--------------------+---------------------------------------------------------------+ Starting order: dbZFSino,dbZFSsata,dbZFS,ucet Alternative user scripts located in ~/bin :jedit,jdestroy,jcreate
#!/usr/bin/env ruby require 'json' require '~/bin/ArrayToTable' def mark_running(t); return "#{C_LIGHT_CYAN}#{t}#{C_NC}"; end def mark_autostart(t); return "#{C_YELLOW}#{t}#{C_NC}";end # it expect that each jail config is located in /jails/JAILNAME/c.conf existing_jails= `grep -h '^[^#]' /jails/*/c.conf 2> /dev/null| grep -h -E '[[:space:]]*[[:alnum:]][[:space:]]*\\{' | tr -d '\\ \\t{'`.split running_jails=JSON.parse(`jls --libxo json`)['jail-information']['jail'].map{ |hsh| hsh['hostname'] } autostart_jails=`sysrc -n jail_list`.strip.split array_table=[[ mark_running("runs")+"/stopped","version", mark_autostart("autostart ip")+"/ip", "note" ]] existing_jails.each{|j| config=File.read("/jails/#{j}/c.conf") ip=/\s*\$ip\s*=\s*"([^"]*)"/.match(config) ip= ip ? ip[1] : "-" note=/\s*\$note\s*=\s*"([^"]*)"/.match(config) note = note ? note[1] : "-" if File.exist?("/jails/#{j}/rootfs/bin/freebsd-version") freeBSDversion=`/jails/#{j}/rootfs/bin/freebsd-version -u`.sub("RELEASE","R").sub("CURRENT","C").sub("STABLE","S").sub("BETA","B").strip else freeBSDversion="-" end running=running_jails.include?(j) is_autostart=autostart_jails.include?(j) array_table << [ running ? mark_running(j) : j,freeBSDversion,is_autostart ? mark_autostart(ip) : ip, note ] } print array_table.to_table puts "Starting order: #{autostart_jails.join(",")}" print "Alternative user scripts located in ~/bin :" bin_directory_content =Dir.entries("/root/bin").delete_if {|x| x.start_with?(".")} blacklisted_bin_content=%w(ArrayToTable.rb jlist odkladiste jmore jailCreateEpair.sh) puts (bin_directory_content-blacklisted_bin_content).join(",")
# It prints centered table in console, does not count color changing sequences as characters. class Array def to_table(header: true) column_sizes = self.reduce([]) do |lengths, row| row.each_with_index.map{|iterand, index| [lengths[index] || 0, iterand.to_s.gsub(/\e[^m]*m/,'').length].max} end head = '+' + column_sizes.map{|column_size| '-' * (column_size + 2) }.join('+') + '+' puts head to_print = self.clone if (header == true) header = to_print.shift print_table_data_row(column_sizes, header) puts head end to_print.each{ |row| print_table_data_row(column_sizes, row) } puts head end private def print_table_data_row(column_sizes, row) row = row.fill(nil, row.size..(column_sizes.size - 1)) row = row.each_with_index.map{|v, i| v = v.to_s + ' ' * (column_sizes[i] - v.to_s.gsub(/\e[^m]*m/,'').length)} puts '| ' + row.join(' | ') + ' |' end end C_NC="\e[0m" # No Color C_BLACK="\e[0;30m";C_GRAY="\e[1;30m";C_RED="\e[0;31m";C_LIGHT_RED="\e[1;31m";C_GREEN="\e[0;32m";C_LIGHT_GREEN="\e[1;32m";C_BROWN="\e[0;33m";C_YELLOW="\e[1;33m";C_BLUE="\e[0;34m";C_LIGHT_BLUE="\e[1;34m";C_PURPLE="\e[0;35m";C_LIGHT_PURPLE="\e[1;35m";C_CYAN="\e[0;36m";C_LIGHT_CYAN="\e[1;36m";C_LIGHT_GRAY="\e[0;37m";C_WHITE="\e[1;37m"
I prefer nice predictable names of interface pairs so had to write script for it. Generated names of epairs will be renamed.
#! /bin/sh jailName="$1" # ifconfig returns new epair name as outpout epairA="$(ifconfig epair create)" # the second intervace has got b at the end epairB="${epairA%?}b" # We rename the interface from the generated name to be te same like jail's name # The maxium lenght of network interface is about 15 character which souhld be enough ifconfig $epairA name A$jailName ifconfig A$jailName up ifconfig bridge0 addm A$jailName up ifconfig $epairB name B$jailName # The line below would only work if called from exec.created section #ifconfig eth0 vnet $jailName up # insipration from https://forums.freebsd.org/threads/jails-vnet-freebsd-mastery-multiple-interfaces.70356/ # to list only epair interfaces you can you can use # ifconfig -g epair -l
opens config of one ore more jails in vim
#! /bin/sh vim_arguments="" while test $# -gt 0 do vim_arguments="$vim_arguments /jails/$1/c.conf" shift done vim -p $vim_arguments
Creates new jail.
#! /bin/sh jailName="$1" mkdir -p /jails/$jailName if zfs list zroot/jails/$jailName then echo " Dataset $jailName already exists !" exit else ls -al /usr/freebsd-dist/MANIFEST echo "I will install freeBSD version which was issued by the day the file above was last changed." read -p "If you want to instlal newer version, remove the content of dirctory /usr/freebsd-dist/ before continuing. " REPLY zfs create -o mountpoint=/jails/$jailName zroot/jails/$jailName # zfs set recordsize=8K zroot/jails/$jailName cd /jails/$jailName # adresář rootfs to samo vytvoří bsdinstall jail rootfs datum=`date "+%Y-%m-%d %H:%M:%S"` printf "$jailName{\n\$note = \"Created $datum\";\n\$ip = \"192.168.144.XXX/24\";\n}">/jails/$jailName/c.conf echo "Doing update patches" freebsd-update -b /jails/$jailName/rootfs fetch freebsd-update -b /jails/$jailName/rootfs install echo "Do not forget to change ip adress in /jails/$jailName/c.conf" echo "Remember to setup firewall." echo "Install apps for example by \"pkg install screen vim mc\" inside." fi
destroys a jail
#! /bin/sh jailName="$1" if ! zfs list zroot/jails/$jailName then echo " dataset zroot/jails/$jailName does not exists " exit fi service jail stop $jailName read -p "Are you sure you want to delete $jailName ? " REPLY echo # (optional) move to a new line if [ "$REPLY" == "Y" ] || [ "$REPLY" == "y" ] then zfs destroy zroot/jails/$jailName sleep 2 rmdir /jails/$jailName fi
$bridge = "bridge0"; $gateway = "XX.XX.XX.XX"; # PATH/HOSTNAME path = "/jails/${name}/rootfs"; host.hostname = "${name}"; # VNET/VIMAGE vnet; vnet.interface = "B$name"; exec.prestart = "/root/bin/jailCreateEpair.sh $name"; # have to remove the mask part of ip in order to by able to use the ip in firewall rules exec.prestart += "(echo -n '# $note\next_if = \"B$name\"\next_ip = \"' && echo -n '${ip}'|cut -d \"/\" -f 1|tr -d '\n' && echo -n '\"\n')> ${path}/etc/pf_local_definitions.conf"; # I mount a common config file direcotry for shared configs and settings exec.prestart += "mkdir -p ${path}/etc/ovps_shared"; exec.prestart += "mount_nullfs -o ro /etc/ovps_shared ${path}/etc/ovps_shared"; exec.start = "ifconfig B$name ${ip} up"; exec.start += "route add -net XX.XX.XX.XX/29 -interface B$name || true"; exec.start += "route add -net XX.XX.XX.XX/28 -interface B$name || true"; exec.start += "route add -net 192.168.145.0/24 81.31.45.55"; exec.start += "route add default ${gateway}"; exec.start += "/bin/sh /etc/rc"; exec.poststop = "ifconfig bridge0 deletem A$name"; exec.poststop += "ifconfig A$name destroy"; exec.release = "umount -t nullfs ${path}/etc/ovps_shared"; # STARTUP/LOGGING exec.stop = "/bin/sh /etc/rc.shutdown"; exec.consolelog = "/var/log/jail_console_${name}.log"; # PERMISSIONS allow.raw_sockets; exec.clean; mount.devfs; devfs_ruleset = 5; .include "/jails/*/c.conf";
Each jail has got very smal own config. For example
pkg{ $note = "Created 2025-06-17 11:14:12"; $ip = "192.168.144.152/24"; }