Bastille automates FreeBSD jails with templates and a bastille.conf
Bastille is an open-source system for automating deployment and management of containerized applications on FreeBSD.
At a glance
- What is it?
- Bastille is a shell based system for deploying and managing containerized applications on FreeBSD, built around bastille templates and a configuration file. The last push was on 2026-10-17 and the newest release is 1.4.4, and the project is only useful on FreeBSD.
- Who is it for?
- Bastille fits a FreeBSD administrator who wants jails created from declarative templates and upgraded through a configuration file rather than by hand, and it is the wrong tool for anyone on Linux or on a host without ZFS. Before adopting it, read the Breaking Changes section for version 1.x if you already run VNET jails, because the epair renaming and the MAC address change affect running services.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A FreeBSD answer to container orchestration, not a Docker wrapper
Bastille describes itself as an open-source system for automating deployment and management of containerized applications on FreeBSD. The wording is precise, and it is worth staying with it. The unit of work is a FreeBSD jail, and the repository topics list manage-freebsd-jails and zfs alongside containers, which places it firmly in the FreeBSD world rather than in the Linux container ecosystem.
The implementation language is Shell, and the repository layout matches a command line tool rather than a service. The top level holds a Makefile, a docs/ directory, a tests/ directory, a Vagrantfile and a usr/ tree, which is the shape of a program that installs its own scripts, configuration and man pages into the system. There is no daemon to run and no API to talk to. You drive it with a command.
The project also keeps a ROADMAP.md, an AUTHORS.md and a CODE-OF-CONDUCT.md, and the README links an official website at bastillebsd.org along with a Read the Docs manual hosted at bastille.readthedocs.io. The split is telling: the README covers installation and the breaking changes that will bite you on upgrade, while the longer form documentation, including the Usage, Getting Started and Comparing chapters, lives elsewhere.
For the intended user, the appeal is repetition. A Bastille template describes a jail, and applying it produces a running service. Doing that for a dozen services by hand with the underlying FreeBSD tools is the work Bastille takes over.
Three install paths, and only one of them is stable
The README gives three ways to install Bastille on FreeBSD, and the third is explicitly marked bleeding edge and unstable, primarily for developers.
The recommended route is the official FreeBSD ports tree, installed with pkg:
pkg install bastilleIf you would rather build from the ports tree yourself, the README clones it shallowly and runs make in the port directory:
git clone https://git.freebsd.org/ports.git --depth 1 /usr/ports
make -C /usr/ports/sysutils/bastille install cleanThe git route clones the project repository and runs make install from the checkout:
git clone https://github.com/bastillebsd/bastille.git
cd bastille
make installThe Makefile is worth reading before you use that last path. The install target rewrites the BASTILLE_VERSION line in usr/local/bin/bastille so the command reports the branch name and the short git revision instead of a release number, copies the usr/ tree into the system, and gzips the man pages for bastille and its sub-commands. The target itself prints 'This method is for testing & development.'
Whatever the route, one extra step is required if you want jails to start at boot, and it is a single rc variable:
sysrc bastille_enable=YESThe uninstall target is equally explicit about what it removes, and it leaves one file behind on purpose, telling you that you may need to remove /usr/local/etc/bastille/bastille.conf manually if it is no longer needed.
Upgrading means merging bastille.conf with the new sample
Bastille's configuration is a single file, and the upgrade path depends on it. The README warns that when upgrading from an earlier version, for example from 0.10.20230714 to 1.2.1.251203, you might need to update your bastille.conf. The procedure given is a diff against the sample that ships with the new version:
cd /usr/local/etc/bastille
diff -u bastille.conf bastille.conf.sampleThe instruction that follows is to merge the lines present in the new bastille.conf.sample into your bastille.conf. So a Bastille upgrade is not a package operation alone. A new version can introduce configuration keys that the older file does not have, and the operator is expected to notice them through the diff and edit by hand.
That is a genuine operational cost and worth weighing before adopting the tool on a host with many jails. There is no documented migration command that rewrites the file for you, and the README does not say what happens if a required key is missing. A silent failure to apply a setting, or a template that errors on an unknown key, are both plausible outcomes of an unmerged config, and neither is documented.
The two files live in the same directory, /usr/local/etc/bastille, and the uninstall target confirms the path by removing bastille.conf.sample from it. Keeping that directory under version control or at least in a backup is a judgement the operator has to make alone.
Every jail gets e0a and e0b interfaces, and long names get truncated
The Breaking Changes section is the most technical part of the README, and it describes a real constraint in the network layer.
Up to version 1.0, Bastille handled epairs for -V jails using the jib script included in FreeBSD installs, but for -B jails it statically assigned an epair to each jail. The consequence was that you could only run one type, -V or -B, of VNET jail on a given system. Starting with version 1.0.20250714, all epairs are handled dynamically, so both types can coexist, and a naming scheme was introduced to go with that change.
The scheme gives every jail a host side and a jail side interface named e0a_jailname and e0b_jailname. Interfaces added later with the network sub-command become e1a_jailname and e1b_jailname, with the number incrementing by one for each additional interface. The stated purpose is to make it easier to work out which jail a given epair belongs to.
The limitation is the interface name length limit. When a jail name is too long, Bastille falls back to e0b_bastille1 and e0b_bastille1 with the number incrementing by one for each new epair, so a jail called mylongjailname ends up as e0a_bastille2 and e0b_bastille2. The fallback is not derived from the jail name at all, which means the interface name no longer identifies the jail, and you have to map it back yourself.
Upgrading is therefore not entirely automatic. Bastille tries to convert each jail's jail.conf and rc.conf to the new syntax, and this happens when the jail is stopped. After updating you must restart all your jails, the README suggests one at a time in case of issues, so the conversion runs. If you added interfaces with the network sub-command, you must edit the jail.conf and rc.conf files yourself to rename the extra interfaces, incrementing the number in the prefix.
VNET jails get new MAC addresses unless -M was used
The Important Limitations subsection names a consequence that is easy to miss. Because of the JIB script used when creating VNET jails, MAC addresses of those jails change. If you have VNET jails created with -V and you did not also use -M when creating them, the MAC addresses will change, because the JIB script generates a MAC address from the jail interface name. If you did use -M, the MAC should stay the same.
The mechanism explains itself once you see it. The interface names changed with the new e0a and e0b scheme, and the MAC is derived from that name, so a new name means a new MAC. The -M option is the escape hatch, because it lets you set the MAC yourself and therefore keep it stable across a rename.
This is the kind of breakage that shows up outside the jail. A jail whose MAC changes may have a different address on the network it advertises, and anything that pinned an old MAC, whether a firewall rule, a switch port configuration or a monitoring check, has to be revisited. The README states the behaviour plainly but does not describe a migration procedure for it, and it does not say whether Bastille can rewrite a MAC on an existing jail.
The practical rule that follows from the documentation is narrow and specific: if you run VNET jails, create new ones with -M so the MAC is explicit, and expect to touch anything that referenced the old addresses when you upgrade past 1.0.20250714.
The project ships with tests, docs and a BSD licence
Bastille's repository is more conventional than a shell script collection usually is. There is a tests/ directory and a Vagrantfile, and the README shows badges for shellcheck and unit tests, both wired to GitHub Actions workflows in the.github/ directory. The.readthedocs.yaml at the top level is what builds the Read the Docs manual linked from the README, and the docs/ directory holds the source for it, including the images the README embeds.
Licensing is BSD-3-Clause, the licence file sits at the top of the repository, and the AUTHORS.md file credits contributors. For a tool that copies scripts into /usr/local/bin, generates rc scripts and writes configuration under /usr/local/etc, a permissive licence is a reasonable choice for operators who want to read and adjust what runs on their machines.
Maintenance is visible in the release history. Versions 1.4.2.260421, 1.4.3.260429 and 1.4.4 all shipped in 2026, with 1.4.4 released on Bastille Day 2026 on 2026-07-12, and the last push was on 2026-10-17. The repository is not archived, and the version numbers carry a date suffix, so you can tell roughly how recent a given build is from its own name.
The version scheme is worth noting because the README's own upgrade example uses it: 0.10.20230714 and 1.2.1.251203 are both dated forms, and the internal BASTILLE_VERSION variable is rewritten to a branch and revision pair when you install from git. The ROADMAP.md file at the top level is where planned work is tracked, and the README does not summarise it, so the direction of the project has to be read there directly.
Editorial conclusion
Bastille fits a FreeBSD administrator who wants jails created from declarative templates and upgraded through a configuration file rather than by hand, and it is the wrong tool for anyone on Linux or on a host without ZFS. Before adopting it, read the Breaking Changes section for version 1.x if you already run VNET jails, because the epair renaming and the MAC address change affect running services. Verify first that the templates you intend to use match the jail conventions of your host, since Bastille's naming scheme for interfaces changed with version 1.0.20250714.
Frequently asked questions
How do I install Bastille on FreeBSD?
The supported route is the FreeBSD ports tree with pkg install bastille. The README also gives a ports tree build with make -C /usr/ports/sysutils/bastille install clean and a git clone of the repository followed by make install, which it marks as bleeding edge and intended for developers.
How do I make Bastille jails start at boot?
Set the rc variable with sysrc bastille_enable=YES. The README lists this as a separate step after installation rather than something the install does for you.
What do I need to do when upgrading Bastille?
Diff your configuration against the new sample with diff -u bastille.conf bastille.conf.sample in /usr/local/etc/bastille and merge the new lines into your bastille.conf. After updating you must restart all your jails, one at a time, so Bastille can convert the jail.conf and rc.conf files.
What happens to interface names and MAC addresses after the 1.0 change?
All epairs are now handled dynamically, with interfaces named e0a_jailname and e0b_jailname and later ones numbered e1a, e1b and so on. Because the JIB script derives a MAC address from the jail interface name, VNET jails created with -V and without -M will have new MAC addresses after the update.
What licence does Bastille use?
The repository is licensed under BSD-3-Clause, with the licence file at the top level of the tree and an AUTHORS.md file crediting contributors.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/bastillebsd-bastille)Was this useful?