Published on: 2025-11-07 12:28:59 by Pertho
Updated on: 2025-11-07 13:40:06 • 8 min read
Disclaimer: I’m still fairly new to FreeBSD jails, so if there’s anything missing, incorrect, or could be done better by all means hit me up on @pertho@mastodon.bsd.cafe.
Running WINE games in a FreeBSD Jail with Bastille BSD
BastilleBSD is a great wrapper built over top of FreeBSD jails which allows you to manage jails. I chose it in this example because it has some really fantastic template abilities which would let me set up the jail with more automation, because let’s face it: automation produces fewer errors once you’ve worked everything out.
FreeBSD jails are containers and as such, as separated from the rest o…
Published on: 2025-11-07 12:28:59 by Pertho
Updated on: 2025-11-07 13:40:06 • 8 min read
Disclaimer: I’m still fairly new to FreeBSD jails, so if there’s anything missing, incorrect, or could be done better by all means hit me up on @pertho@mastodon.bsd.cafe.
Running WINE games in a FreeBSD Jail with Bastille BSD
BastilleBSD is a great wrapper built over top of FreeBSD jails which allows you to manage jails. I chose it in this example because it has some really fantastic template abilities which would let me set up the jail with more automation, because let’s face it: automation produces fewer errors once you’ve worked everything out.
FreeBSD jails are containers and as such, as separated from the rest of the system along with a filesystem “chroot” which means even if you managed to have some dodgy code that ran, at least it would only wipe out or mess up the jail and not get to the rest of your system!
In this guide I’m going to assume a few things:
- You are using ZFS as your filesystem
- You are comfortable with the FreeBSD command-line and editing files
- Using FreeBSD 14.3-RELEASE which is still, at the time of this writing, the latest release. 15.0 is just around the corner (less than a month away)
- I use
doasinstead ofsudoin order to run commands as root. Feel free to substitute as needed. - You are using Xorg and not Wayland.
Set up Bastille BSD
Please take some time and read through and follow the guide for setting up Bastille with the getting started section of their website. It’s very thorough and explains it a whole lot better.
For the modifications I made to /usr/local/etc/bastille/bastille.cfg these are the lines of my config:
bastille_bootstrap_archives="base lib32"
bastille_zfs_enable="YES"
bastille_zfs_zpool="zroot"
My ZFS pool name is the default FreeBSD uses: zroot. I also added lib32 to the bootstrap archives because WINE needs the lib32 component to work.
Here are the relevant lines of my /etc/rc.conf for Bastille:
bastille_enable="YES"
cloned_interfaces="lo1"
ifconfig_lo1_name="bastille0"
No fancy stuff here. Just using the NAT on the loopback interface. If you want to set up a VNET jail, go for it. I wanted to use the simplest example here.
pf.conf
This is the simple pf.conf I use on the host:
ext_if="wlan0"
set block-policy return
scrub on $ext_if all fragment reassemble
set skip on lo
table <jails> persist
nat on $ext_if from <jails> to any -> ($ext_if:0)
rdr-anchor "rdr/*"
block in all
pass out quick keep state
antispoof for $ext_if inet
pass in inet proto tcp from any to any port ssh flags S/SA keep state
pass inet proto icmp
pass inet6 proto icmp6
I’m using a laptop for this, so the egress network interface is wlan0. The pf.conf is very simple and generic and lets out all outbound traffic. If you’re playing games that talk to servers, then you’ll probably want this. Edit to suit your security needs. At a minimum, if you do egress filtering, you want outbound 80/443 for the pkg installs and any other ports needed.
Special jail permissions
We need to create a set of rules for the jail so it can use the 3D graphics and sound devices since, by default it normally wouldn’t have access to those.
Many thanks to Joel Carnat for the inspiration for these rules in his great blog post Running Web Browsers in FreeBSD Jail.
Create (or modify) the file /etc/devfs.conf and add this:
[devfsrules_gaming_jail=70]
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add path 'mixer*' unhide
add path 'dsp*' unhide
add path dri unhide
add path dri/* unhide
add path drm unhide
add path drm/* unhide
add path pci unhide
Setting up the Jail
Now that Bastille is set up and we have a devfs rule ready to go for the jail, let’s set up the jail itself.
Create the template file (Bastillefile)
In this example, I’ll be using pertho as the top level name of the Bastille templates and gaming as the template that will be applied:
doas mkdir -p /usr/local/bastille/templates/pertho/gaming
Next, let’s create /usr/local/bastille/templates/pertho/gaming/Bastillefile which has all the magic in it for setting up the jail:
ARG jail_user=gamer
ARG host_user=pertho
CONFIG set enforce_statfs=1;
CONFIG set allow.mount.fdescfs;
CONFIG set allow.mount.procfs;
CONFIG set mount.procfs;
CONFIG set devfs_ruleset=70
RESTART
SYSRC clear_tmp_enable=NO
SYSRC clear_tmp_X=NO
PKG oksh winetricks webfonts twemoji-color-font-ttf xorg mesa-libs mesa-dri wine-devel winetricks wine-mono-devel wine-gecko-devel
CMD pw useradd -n ${jail_user} -G video -L default -m -s /usr/local/bin/oksh -w none
CMD mkdir -p /home/${jail_user}/.config /home/${jail_user}/.local/share/fonts /home/${jail_user}/.config
CMD chown -R ${jail_user}:${jail_user} /home/${jail_user}
MOUNT /home/${host_user}/.Xauthority /usr/home/${jail_user}/.Xauthority nullfs ro 0 0
MOUNT /tmp/.X11-unix /tmp/.X11-unix nullfs rw 0 0
MOUNT /var/run/dbus /var/run/dbus nullfs rw 0 0
CMD su - ${jail_user} /usr/local/share/wine/pkg32.sh install -y wine-devel mesa-dri gstreamer1-plugins-good gstreamer1-plugins-ugly gstreamer1-libav
CP usr /
This assumes your host user is pertho but the username in the jail will be gamer. I am also assuming both users will be uid 1001 and gid 1001. Since we are nullfs mounting files from the host user’s home directory, we want the uid/gid to be the same. I’m adding .local/share/fonts to this since I keep some personal fonts in there and would like to share them with the jail. This is completely optional. The only files you absolutely need are $HOME/.Xauthority and /tmp/.X11-unix as these are required for the X Windows system.
I am adding procfs mounting to the jail as well since there are some WINE games I’ve run in the past that need some Linuxisms. Note that clear_tmp_enable and clear_tmp_X are both set to NO here. This is important as we need to share some files between the jail and the host.
I chose wine-devel because the version at time of writing: 10.15 is really very stable. I don’t know if WINE 9.0-proton is recent enough to run most things and wine is 10.1 or 10.2 which is before a lot of bug fixes got into the WINE 10 code base. If you do want to run wine-proton, the pkg32.sh install line will need to be modified appropriately as well as the PKG line.
Extra files to deploy
Since we have CP usr / as the last line, I wanted to provide those files as well. They’re basically .profile and .kshrc. The Bastillefile does install oksh which is OpenBSD’s ksh. This is the shell I prefer to use. If you prefer bash, zsh, fish, or even csh/tcsh, feel free to modify as needed.
doas mkdir -p /usr/local/bastille/templates/pertho/gaming/usr/home/gamer to create the directory
I created /usr/local/bastille/templates/pertho/gaming/usr/home/gamer/.profile:
[ -f $HOME/.kshrc ] && export ENV=$HOME/.kshrc
[ -d $HOME/bin ] && PATH=$PATH:$HOME/bin
[ -d $HOME/.local/bin ] && export PATH="$PATH:$(du "$HOME/.local/bin" | cut -f2 | paste -sd ':' -)"
export DISPLAY=:0
export XAUTHORITY="$HOME/.Xauthority"
And this is /usr/local/bastille/templates/pertho/gaming/usr/home/gamer/.kshrc:
export XDG_CONFIG_HOME="$HOME/.config"
export XDG_DATA_HOME="$HOME/.local/share"
export XDG_CACHE_HOME="$HOME/.cache"
Create the Jail
I created the Jail like so:
doas bastille bootstrap 14.3-RELEASE update
doas bastille create gaming 14.3-RELEASE 10.50.0.2/24
doas bastille template gaming pertho/gaming
You’ll see about 270 packages get installed in the jail. The pkg32.sh script, which sets up the 32-bit bits of WINE installs a bunch more. Let this run until complete. Once it’s done, you will want to edit the jail to use our devfs rule.
Allow X connections from jail to host
OK this may not be the most elegant approach to security, but I just opened a terminal on the host and did: xhost + which lets any host connect to X Windows. Obviously, you don’t want to run this on a machine that has a direct connection to the internet so make sure your /etc/pf.conf is secure enough.
I think there’s a better way to do this which involves adding the jail to connect with xauth but I’ve not yet done this, so for now just running xhost + will do what you need.
There a (good writeup on the FreeBSD Forums about this)[https://forums.freebsd.org/threads/x11-apps-inside-jail-authorization-required-but-no-authorization-protocol-specified.95604/] which includes a shell script which can add the access.
Set up WINE in the Jail
I ran doas bastille console gaming and then ran su - gamer to get into the gamer user’s shell in the Jail. If you installed the .kshrc / .profile files as above (or their equivalents), check your environment to make sure DISPLAY and XAUTHORITY variables are set:
gaming$ env | grep -E '(DISPLAY|XAUTHORITY)'
DISPLAY=:0
XAUTHORITY=/home/gamer/.Xauthority
Now we can set up the WINE prefix. I’m not actually setting the WINEPREFIX variable because the default ($HOME/.wine) is sufficient for my needs.
Run: wine winecfg and it should display the WINE configuration panel where you can set up things like the drives it sees (C: and Z:), audio, desktop settings, etc..
Next, I ran winetricks -q corefonts to install the core Microsoft fonts. If your game requires any components that winetricks provides, this is the time to install them.
If you have any games to set up, you can install them here. You should then be able to run them in wine. I set up a little shell script like this:
export WINEDEBUG=-all
cd "$HOME/.wine/drive_c/Program Files (x86)/Path/to/Game"
wine game.exe
And that’s it! You can now run WINE games inside a jail!
Vulkan / dxvk
I did try to get Vulkan dxvk working in WINE, but for whatever reason, winetricks dxvk failed to compile it. Reading around the FreeBSD forums and subreddit, people suggested WINE 9.0-proton was the place to get Vulkan/dxvk working but I wanted the latest WINE 10.x release features/bugfixes. Perhaps someone knows how to get dxvk working in WINE 10.x on FreeBSD. (The winetricks dxvk command does work on Linux but not on FreeBSD)
Conclusion
Initially, I thought this was going to be a pretty difficult and require a lot of trial and error but I was surprised at how easy it was to get this all working. I was really happy to get some of my favorite games working in a FreeBSD Jail, and having ZFS snapshots around was a great way to test things in case I needed to backtrack.
Happy gaming!