Today – as my son requested – we will talk about Minecraft server … inside FreeBSD Jails container.

This is kinda like Docker/Podman thing on Linux – but secure instead.
Prepare
First we will populate /jail path and also fetch needed FreeBSD Base System version.
As for today – we will use 14.2-RELEASE version of FreeBSD UNIX.
host # mkdir -p /jail/minecraft /jail/BASE host # VER=$( freebsd-version | awk -F '-' '{print $1 "-" $2}' ) host # fetch -o /jail/BASE/${VER}-base.txz https://download.freebsd.org/releases/amd64/${VER}/base.txz
Next we will create our dedicated Minecraft FreeBSD Jail and populate it with its own Base System contents.
We will also copy /var/run/dmesg.boot as Minecraft server looks for it.
host # tar -C /jail/minecraft --unlink -xvf /jail/BASE/${VER}-base.txz host # cp /var/run/dmesg.boot /jail/minecraft/var/run/
Create
Now we will setup base FreeBSD Jails configuration.
These will be the default for all other Jails unless we redefine them.
host # cat /etc/jail.conf
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
exec.clean;
mount.devfs;
Now out Minecraft Jail config.
We will use em0 LAN interface and 10.0.0.210 IP address.
host # cat /etc/jail.conf.d/minecraft.conf minecraft { # GLOBAL exec.start = "/bin/sh /etc/rc"; exec.stop = "/bin/sh /etc/rc.shutdown"; exec.consolelog = "/var/log/jail_console_${name}.log"; exec.clean; mount.devfs; host.hostname = ${name}; path = /jail/${name}; # CUSTOM
ย ip4.addr = 10.0.0.210;
interface = em0;
allow.raw_sockets;
allow.sysvipc;
devfs_ruleset=210;
allow.mount;
enforce_statfs=1;
allow.mount.devfs;
allow.mount.procfs;
allow.mount.fdescfs; }
Below you will also find /etc/devfs.rules ruleset on the host below.
host # grep -A 4 minecraft /etc/devfs.rules
[minecraft=210]
add include $devfsrules_jail
add path 'fd*' unhide
We can now start our Jail.
host # service jail onestart minecraft Starting jails: minecraft. host # jls JID IP Address Hostname Path 1 10.0.0.210 minecraft /jail/minecraft
You may as well use mine jmore(8) tool.
host # jmore JAIL JID TYPE VER DIR IFACE IP(s) ---- --- ---- --- --- ----- ----- classic - std 13.2-R /jail/classic em0 10.0.0.199 ctld-two - vnet 13.2-R /jail/ctld-two ${if}b - ctld - vnet 13.2-R /jail/ctld ${if}b - fbsdjail - std 13.1-R /jail/fbsdjail wlan0 10.0.0.43 iscsi - vnet 13.2-R /jail/iscsi ${if}b - minecraft 1 std 14.2-R /jail/minecraft em0 10.0.0.210 minio - std 14.0-R /jail/minio em0 10.0.0.133 nfsd - vnet 14.1-R /jail/nfsd ${if}b - other - std 14.1-R /jail/other em0 10.0.0.199 sambajail - vnet 14.1-R /jail/sambajail ${if}b - unfs3 - vnet 14.1-R /jail/unfs3 ${if}b -
To make that Minecraft Jail permanently start at boot something like that below would be needed on the host system in the /etc/rc.conf file.
host # grep jail /etc/rc.conf
jail_enable=YES
jail_devfs_enable=YES
jail_list="minecraft"
Configure FreeBSD Jail
Now we will enter the Minecraft Jail.
The jmore minecraft c is equivalent to the well known env PS1='minecraft # ' jexec minecraft /bin/sh command.
host # jmore minecraft c
minecraft #
Next some basic things such as DNS or switching to latest branch for pkg(8) FreeBSD package manager.
minecraft # echo nameserver 1.1.1.1 > /etc/resolv.conf minecraft # mkdir -p /usr/local/etc/pkg/repos minecraft # sed -e 's|quarterly|latest|g' /etc/pkg/FreeBSD.conf > /usr/local/etc/pkg/repos/FreeBSD.conf minecraft # pkg search -o minecraft games/minecraft-client Client for the block building game
Now we will install all other needed packages – as Minecraft server needs to be build using FreeBSD Ports.
minecraft # pkg install gitup bsddialog ccache4 portconfig openjdk21 tmux jless
As we need to build Minecraft server from the FreeBSD Ports because of license that needs to be accepted (or ignored) manually – we will now need to fetch the FreeBSD Ports tree with gitup tool.
At the make config part choose DAEMON option.
minecraft # gitup ports (...) # # Please review the following file(s) for important changes. # /usr/ports/UPDATING # /usr/ports/mail/dspam/files/UPDATING # # Done. minecraft # cd /usr/ports/games/minecraft-server minecraft # make config +------------|minecraft-server-1.21.4|--------------+ | 'F1' for Ports Collection help. | | +---------- RUN [select at least one] ----------+ | | | new (*) DAEMON Run as a service | | | | new ( ) STANDALONE Run the .jar file directly | | | +-----------------------------------------------+ | | [ OK ] [Cancel] | +---------------------------------------------------+
Build
Next we will build Minecraft server.
minecraft # echo DISABLE_LICENSES=yes >> /etc/make.conf minecraft # env BATCH=yes make build install clean (...) When you first run minecraft-server, it will populate the file /usr/local/etc/minecraft-server/eula.txt It is required to read the EULA, and then set eula=true - Configuration files can be found in /usr/local/etc/minecraft-server/ - Log and debug output files can be found in /var/log/minecraft-server/ - World files can be found in /var/db/minecraft-server/ Without daemon option: - To run the server, run /usr/local/bin/minecraft-server - To edit java's parameters, edit /usr/local/etc/minecraft-server/java-args.txt - To run with a specific version of Java, set environment variable JAVA_VERSION, for example: export JAVA_VERSION=22 /usr/local/bin/minecraft-server or: JAVA_VERSION=22 /usr/local/bin/minecraft-server With daemon option: - The service has been installed with the name 'minecraft' - To adjust maximum memory usage (-Xmx), use minecraft_memx= in /etc/rc.conf - To adjust initial memory usage (-Xms), use minecraft_mems= in /etc/rc.conf - To add other java parameters, use minecraft_args= in /etc/rc.conf - To run with a specific version of Java, use minecraft_java_version= in /etc/rc.conf - To see the interactive console, type service minecraft console ===> Cleaning for minecraft-server-1.21.4
Minecraft Server Configuration
As suggested in the pkg-message we will add additional virtual filesystems to the /etc/fstab file.
We will also make sure they are mounted at Jail start time with /etc/rc.local file.
minecraft # cat << FSTAB >> /etc/fstab fdesc /dev/fd fdescfs rw 0 0 proc /proc procfs rw 0 0 FSTAB minecraft # echo 'mount -a' >> /etc/rc.local minecraft # mount -a minecraft # mount zroot/jail on / (zfs, local, noatime, nfsv4acls) devfs on /dev (devfs) fdescfs on /dev/fd (fdescfs) procfs on /proc (procfs, local) devfs on /dev (devfs)
We will not configure Minecraft Jail main /etc/rc.conf config file with needed Minecraft server options.
We will also ‘accept’ EULA and create basic Minecraft server config at /usr/local/etc/minecraft-server/server.properties file.
You may also configure additional Java parameters in the /usr/local/etc/minecraft-server/java-args.txt file.
Increase the values if its too small for your case.
minecraft # cat << RC >> /etc/rc.conf minecraft_enable=YES minecraft_mems=1024M minecraft_memx=1024M RC minecraft # echo eula=true > /usr/local/etc/minecraft-server/eula.txt minecraft # cat << MINECRAFT > /usr/local/etc/minecraft-server/server.properties enable-jmx-monitoring=false rcon.port=25575 level-seed= gamemode=survival enable-command-block=false enable-query=false generator-settings={} enforce-secure-profile=true level-name=world motd=FreeBSD Minecraft Server query.port=25565 pvp=true generate-structures=true max-chained-neighbor-updates=1000000 difficulty=easy network-compression-threshold=256 max-tick-time=60000 require-resource-pack=false use-native-transport=true max-players=20 online-mode=false enable-status=true allow-flight=false initial-disabled-packs= broadcast-rcon-to-ops=true view-distance=10 server-ip= resource-pack-prompt= allow-nether=true server-port=25565 enable-rcon=false sync-chunk-writes=true resource-pack-id= op-permission-level=4 prevent-proxy-connections=false hide-online-players=false resource-pack= entity-broadcast-range-percentage=100 simulation-distance=10 rcon.password= player-idle-timeout=0 force-gamemode=false rate-limit=0 hardcore=false white-list=false broadcast-console-to-ops=true spawn-npcs=true spawn-animals=true log-ips=true function-permission-level=2 initial-enabled-packs=vanilla level-type=minecraft\:normal text-filtering-config= spawn-monsters=true enforce-whitelist=false spawn-protection=16 resource-pack-sha1= max-world-size=29999984 MINECRAFT
Start
Now it is the time to start the installed and configured Minecraft server.
minecraft # service minecraft start Starting minecraft. minecraft # service minecraft status minecraft is running. minecraft # sockstat -l4 USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS mcserver java 33227 103 tcp4 10.0.0.210:25565 *:* root syslogd 7809 5 udp4 10.0.0.210:514 *:*
It seems to be running properly – but in case it does not – try this command for the debug purposes.
minecraft # su mcserver -c '/usr/local/bin/java -Xmx1024M -Xms1024M -jar /usr/local/minecraft-server/server.jar nogui'
Connect with Minecraft Client
First – make sure that your client is in the same version as the Minecraft server version – 1.21.4 in my case.
In my case the Minecraft client is started from some random Windows box – as shown below.

Hit Multiplayer button.

Now hist Add Server button – so we will add out FreeBSD based Minecraft server.
Enter your preferred Minecraft server name and the IP address.

After a moment our FreeBSD based Minecraft server will appear on the list.

Now hit the Join Server button to join it.

We will see the Connecting to the server… message.
… and after a moment we are joined to our Minecraft server.

Users
As I am not a Minecraft expert it took me a little to find out a way to define ‘admins’ on that server.
Fortunately I have a working solution ๐
When I was writing about mine jmore(8) tool here New jmore(8) FreeBSD Jails List/Manage Tool I initially called it jless(8) but someone notified my that there is already a tool with that name and that its for JSON. We will use it in a moment ๐
When someone connects to our server its name and uuid is being added to the /usr/local/etc/minecraft-server/usercache.json file as shown below.
host # cat /jail/minecraft/usr/local/etc/minecraft-server/usercache.json | tr ',' '\n' [{"name":"antuan" "uuid":"0d61326c-dfd1-3fa8-ba9d-249d402fb700" "expiresOn":"2025-05-05 14:04:15 +0000"} {"name":"antek" "uuid":"4b520bac-4b31-3c41-8f9b-2781763e5c88" "expiresOn":"2025-05-05 09:16:08 +0000"}]
host # jless /jail/minecraft/usr/local/etc/minecraft-server/usercache.json | cat [ { "name": "antuan", "uuid": "0d61326c-dfd1-3fa8-ba9d-249d402fb700", "expiresOn": "2025-05-05 14:04:15 +0000" }, { "name": "antek", "uuid": "4b520bac-4b31-3c41-8f9b-2781763e5c88", "expiresOn": "2025-05-05 09:16:08 +0000" } ]
It looks even better with colors from bat(1) command.

Now – to make these users ‘admins’ we need to add them to the /usr/local/etc/minecraft-server/ops.json file and also restart Minecraft server to make it effective.
host # jless /jail/minecraft/usr/local/etc/minecraft-server/ops.json | cat [ { "uuid": "4b520bac-4b31-3c41-8f9b-2781763e5c88", "name": "antek", "level": 4,
"bypassesPlayerLimit": false
}, { "uuid": "0d61326c-dfd1-3fa8-ba9d-249d402fb700", "name": "antuan", "level": 4,
"bypassesPlayerLimit": false } ]
Level 4 is the highest level of permissions for the record.

Now my son has all needed commands that he uses like /gamemode or /time for example ๐
Summary
Feel free to add your thoughts about other needed configurations about personal Minecraft server.
UPDATE 1 – FreeBSD Jails versus Linux Podman
I do not expected that first sentence will be the reason for most comments – so I also add some details here. Lets have a discussion about differences between security of FreeBSD Jails and Linux Podman containers.
Isolation: With rootless Podman it seems to be on the same level as Jails – but only if You run Podman with SELinux/AppArmor enabled. Without SELinux/AppArmor the Jails offer better isolation. When you run Podman with SELinux/AppArmor and then you add MAC Framework (like mac_sebsd/mac_jail/mac_bsdextended/mac_portacl) the Jails are more isolated again.
Kernel Syscalls Surface: Even rootless Podman has ‘full’ syscall access unless blocked by seccomp (SELinux). Jails have restricted use of syscalls without any additional tools – and that can be also narrowed even more with MAC Framework on FreeBSD.
Firewall: You can not run firewall inside rootless Podman container. You can run entire network stack and any firewall like PF or IPFW independently from the host inside VNET Jail – which means more security.
TL;DR: FreeBSD Jails are generally more secure out-of-the-box compared to Podman containers and even more secure if you take the time to add additional layers of security.
Time on the market is also important factor.
Jails are in production since 1999/2000 when they were introduced – so 25 years strong – very well battle tested. Docker is with us since 2014 so that means about 10 years less – but we must compare Jails to Podman.ย Rootless support for Podman first appeared late 2019 (in 1.6 version) so only less then 6 years on the market.
That means Jails are the most battle tested of all of them.
EOF
Pingback: ๅจFreeBSD Jailsๅฎนๅจไธญ็Minecraftๆๅกๅจ - ๅๆง็็ ๅ
Pingback: Are FreeBSD Jails a Containers? | ๐๐๐๐๐๐๐๐
Pingback: Valuable News – 2025/04/14 | ๐๐๐๐๐๐๐๐
Pingback: FreeBSD Jails Security | ๐๐๐๐๐๐๐๐