You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -26,7 +26,9 @@ Get daemonless containers running on FreeBSD in 5 minutes.
26
26
!!! tip "Customize Your Guide"
27
27
Scroll to [Interactive Configuration](#interactive-configuration) at the bottom to set your PUID, PGID, and paths. All commands will update automatically.
28
28
29
-
## Prerequisites
29
+
## Podman
30
+
31
+
### Prerequisites
30
32
31
33
!!! failure "Root Privileges Required"
32
34
**Podman on FreeBSD currently requires root.** Rootless mode is not yet supported. All commands in this guide must be run as root (or via `sudo`/`doas`).
@@ -41,9 +43,9 @@ pkg install podman-suite
41
43
Currently, a temporary patch for `ocijail` is required for .NET applications (Radarr/Sonarr).
42
44
See [ocijail patch](ocijail-patch.md).
43
45
44
-
## Host Configuration
46
+
###Host Configuration
45
47
46
-
### 1. Enable Networking
48
+
####1. Enable Networking
47
49
Configure the kernel to allow packet filtering for local traffic and ensure `fdescfs` is mounted.
48
50
49
51
```bash
@@ -56,7 +58,7 @@ mount -t fdescfs fdesc /dev/fd
56
58
echo'fdesc /dev/fd fdescfs rw 0 0'>> /etc/fstab
57
59
```
58
60
59
-
### 2. Configure Firewall (`pf.conf`)
61
+
####2. Configure Firewall (`pf.conf`)
60
62
Add the following to `/etc/pf.conf`. Replace `@INTERFACE@` if your external interface is different.
61
63
62
64
```
@@ -76,14 +78,14 @@ Reload the configuration:
76
78
pfctl -f /etc/pf.conf
77
79
```
78
80
79
-
### 3. Start Podman
81
+
####3. Start Podman
80
82
81
83
```bash
82
84
sysrc podman_enable=YES
83
85
service podman start
84
86
```
85
87
86
-
## Run Your First Container
88
+
###Run Your First Container
87
89
88
90
We'll start with **Tautulli**, a lightweight Python app that doesn't require special permissions.
89
91
@@ -102,7 +104,7 @@ podman logs -f tautulli
102
104
```
103
105
Access the UI at: `http://localhost:8181`
104
106
105
-
## .NET Applications
107
+
###.NET Applications
106
108
Applications like **Radarr** and **Sonarr** require the `allow.mlock` jail annotation to function correctly on FreeBSD.
107
109
108
110
```bash
@@ -114,7 +116,7 @@ podman run -d --name radarr \
114
116
@REGISTRY@/radarr:latest
115
117
```
116
118
117
-
## Advanced Setup (Optional)
119
+
###Advanced Setup (Optional)
118
120
119
121
=== "ZFS Storage"
120
122
If you're using ZFS, configure Podman to use it for proper copy-on-write layering and snapshot support:
@@ -135,13 +137,318 @@ podman run -d --name radarr \
135
137
```
136
138
See [Networking Guide](networking.md) for details.
137
139
140
+
## AppJail
141
+
142
+
### Prerequisites
143
+
144
+
!!! warning
145
+
This quick start guide is intended exclusively for OCI container users. For more general information, see the [AppJail Handbook](https://appjail.readthedocs.io), `appjail-tutorial(7)`, `director(1)` and `director-spec(5)` man pages.
146
+
147
+
!!! warning
148
+
If you plan to use ZFS, set it up before running AppJail. See below for details.
149
+
150
+
Install AppJail and Director
151
+
152
+
```bash
153
+
pkg install -y appjail sysutils/py-director
154
+
```
155
+
156
+
### Host Configuration
157
+
158
+
#### 0. Configure trusted users (optional)
159
+
160
+
AppJail requires privileges to run, but it can be integrated with tools such as [security/doas](https://freshports.org/security/doas) to run it as a user without root privileges. This is recommended when you are the only person using the computer and have privileges, or in cases where there are more than two sysadmins or developers on the same server with root access.
161
+
162
+
**/usr/local/etc/doas.conf**:
163
+
164
+
```
165
+
permit nopass keepenv :appjail as root cmd appjail
166
+
```
167
+
168
+
This rule also assumes that you have a group named `appjail`. If you don't, don't worry:
169
+
170
+
```bash
171
+
pw groupadd -n appjail
172
+
```
173
+
174
+
To add your user to the `appjail` group simply run the following:
175
+
176
+
```bash
177
+
pw groupmod -n appjail -m "$USER"
178
+
```
179
+
180
+
Where `$USER` is your user. For these changes to take effect, you must log back into the system if you are adding yourself.
181
+
182
+
Similarly, there is a variant for `appjail-config` named `appjail-config-user`. The instructions for using it are similar to the above:
183
+
184
+
```
185
+
permit nopass :appjail as root cmd appjail-config
186
+
```
187
+
188
+
To test the changes above, simply run the following as a non-root user:
189
+
190
+
```bash
191
+
appjail help
192
+
appjail-config-user help
193
+
```
194
+
195
+
See also: [Trusted Users on AppJail Handbook](https://appjail.readthedocs.io/en/latest/trusted-users/).
196
+
197
+
#### 1. Enable Networking
198
+
199
+
!!! tip
200
+
Not all network options require packet filtering (for example: aliasing, bridge-only, vnet-only, etc.), but it is particularly useful for Virtual Networks, a common network option in deployments.
201
+
202
+
AppJail does not require any configuration, as it uses the default settings, but we recommend that you at least configure the `EXT_IF` parameter to point to your external interface.
203
+
204
+
**/usr/local/etc/appjail/appjail.conf**:
205
+
206
+
```
207
+
EXT_IF=@INTERFACE@
208
+
```
209
+
210
+
NAT and port forwarding require IP forwarding, so let's set it up:
211
+
212
+
```bash
213
+
sysrc gateway_enable="YES"
214
+
sysctl net.inet.ip.forwarding=1
215
+
```
216
+
217
+
#### 2. Configure Firewall (pf.conf)
218
+
219
+
AppJail uses anchors like other applications that use `pf(4)` as a backend. Just enable `pf(4)` in your `rc.conf(5)`, put the anchors in the `pf.conf(5)` file and reload the rules.
220
+
221
+
```bash
222
+
# Enable pf(4):
223
+
sysrc pf_enable="YES"
224
+
sysrc pflog_enable="YES"
225
+
# Put the anchors in pf.conf(5):
226
+
cat << "EOF" >> /etc/pf.conf
227
+
nat-anchor "appjail-nat/jail/*"
228
+
nat-anchor "appjail-nat/network/*"
229
+
rdr-anchor "appjail-rdr/*"
230
+
EOF
231
+
# Reload the pf(4) rules:
232
+
service pf reload
233
+
# Or restart the rc(8) script if you don't have pf(4) started:
234
+
service pf restart
235
+
service pflog restart
236
+
```
237
+
238
+
See also: [Packet Filter on AppJail Handbook](https://appjail.readthedocs.io/en/latest/networking/packet-filter/).
239
+
240
+
#### 3. Start AppJail
241
+
242
+
If you want to start your jails at startup, enable AppJail's `rc(8)` script:
243
+
244
+
```
245
+
sysrc appjail_enable=YES
246
+
```
247
+
248
+
### .NET Applications
249
+
250
+
In an `appjail-template(5)` file, you can define any `jail(8)` parameter, including the one used by .NET applications.
2026-03-22 04:49:07 - ERROR :: MainThread : Tautulli PlexTV :: PlexTV called, but no token provided.
281
+
2026-03-22 04:49:07 - ERROR :: MainThread : Tautulli PlexTV :: PlexTV called, but no token provided.
282
+
2026-03-22 04:49:08 - INFO :: MainThread : Tautulli WebStart :: Initializing Tautulli web server...
283
+
2026-03-22 04:49:08 - WARNING :: MainThread : Tautulli WebStart :: Web server authentication is disabled!
284
+
2026-03-22 04:49:08 - INFO :: MainThread : Tautulli WebStart :: Thread Pool Size: 10.
285
+
2026-03-22 04:49:08 - INFO :: MainThread : Tautulli WebStart :: Starting Tautulli web server on http://0.0.0.0:8181/
286
+
/app/tautulli/lib/cherrypy/process/servers.py:410: UserWarning: Unable to verify that the server is bound on 8181
287
+
warnings.warn(msg)
288
+
2026-03-22 04:49:13 - INFO :: MainThread : [22/Mar/2026:04:49:13] ENGINE Serving on http://0.0.0.0:8181
289
+
2026-03-22 04:49:18 - INFO :: MainThread : Tautulli is ready!
290
+
```
291
+
292
+
Access the UI at `http://10.0.0.3:8181`
293
+
294
+
**Notes**:
295
+
296
+
1.`-d`: The process will run in the background.
297
+
2.`-u root`: We run `s6` as root, but keep in mind that the process is already running as the `bsd` user inside the jail, which is also mapped based on the `PUID` and `PGID` environment variables. We recommend specifying the user explicitly. See [this pr](https://github.com/daemonless/dbuild/pull/7) for more details.
298
+
3.`-o overwrite=force`: Destroy the jail if it already exists, so that AppJail will recreate it instead of refusing to do so.
299
+
4.`-o virtualnet=":<random> default" -o nat -o expose="8181:8181"`: Network options. In this case, we chose to use Virtual Networks.
300
+
5.`-o container="args:--pull"`: Let's pull the image every time `buildah(1)` detects changes, so that AppJail always runs the jail using the latest image.
301
+
6.`-o ephemeral`: Mark this jail as ephemeral, so that when it stops (or starts, in the event of a power outage on the computer), AppJail will destroy it.
302
+
7.`@REGISTRY@/tautulli:latest tautulli`: The image, tag, and the jail name. The tag is optional.
303
+
304
+
### AppJail Director
305
+
306
+
Although you can use AppJail exclusively to deploy containers, it is recommended that you use AppJail Director. Environment variables set by `appjail-oci(1)``run` will not be preserved after restarting the jail. You can use various `appjail-oci(1)` subcommands, such as `set-user`, `set-env`, etc., and then run the `from` subcommand, but this does not scale well when there are multiple containers. Another advantage is that Director defines its deployment file in YAML format declaratively, so it can be easily shared.
307
+
308
+
For example, the above deployment can easily be translated into a Director file:
309
+
310
+
**appjail-director.yml**:
311
+
312
+
```yaml
313
+
options:
314
+
- virtualnet: ':<random> default'
315
+
- nat:
316
+
services:
317
+
tautulli:
318
+
name: tautulli
319
+
options:
320
+
- expose: '8181:8181'
321
+
- container: 'boot args:--pull'
322
+
oci:
323
+
user: root
324
+
environment:
325
+
- PUID: @PUID@
326
+
- PGID: @PGID@
327
+
volumes:
328
+
- config: /config
329
+
volumes:
330
+
config:
331
+
device: '@CONTAINER_CONFIG_ROOT@/tautulli'
332
+
```
333
+
334
+
**Makejail**:
335
+
336
+
```
337
+
ARG tag=latest
338
+
339
+
OPTION overwrite=force
340
+
OPTION from=@REGISTRY@/tautulli:${tag}
341
+
```
342
+
343
+
**.env**:
344
+
345
+
```
346
+
DIRECTOR_PROJECT=tautulli
347
+
```
348
+
349
+
By default, `director(1)` uses `Makejail` (which is assumed to be in the same directory as the Director file) as its `appjail-makejail(5)` and executes it. Some options are defined in a `appjail-makejail(5)` file, while others are defined per service. The convention is to specify options that do not change frequently in a `appjail-makejail(5)` file and the rest per service in the Director file. You can also set parameters using `ARG`, as we did above to specify the image tag, whose default value is `latest`. Finally, we define a `.env` file with environment variables loaded by Director.
350
+
351
+
**Console**:
352
+
353
+
```
354
+
# appjail-director up
355
+
Starting Director (project:tautulli) ...
356
+
Creating tautulli (tautulli) ... Done.
357
+
- Configuring the user (OCI) ... Done.
358
+
- Configuring the environment (OCI):
359
+
- PUID ... Done.
360
+
- PGID ... Done.
361
+
Starting tautulli (tautulli) ... Done.
362
+
Finished: tautulli
363
+
# appjail-director info
364
+
tautulli:
365
+
state: DONE
366
+
last log: /root/.director/logs/2026-03-22_19h02m04s
367
+
locked: false
368
+
services:
369
+
+ tautulli (tautulli)
370
+
# ls /root/.director/logs/2026-03-22_19h02m04s/tautulli/
[00:00:05] [ info ] [tautulli] Detached: pid:83091, log:jails/tautulli/container/2026-03-22.log
374
+
```
375
+
376
+
### Advanced Setup (Optional)
377
+
378
+
=== "ZFS Storage"
379
+
To enable ZFS, simply add `ENABLE_ZFS=1` to your `appjail.conf(5)` file. You may also need to configure `ZPOOL`, `ZROOTFS`, and `ZOPTS` if the default values do not suit your environment.
380
+
381
+
See also: [ZFS on AppJail Handbook](https://appjail.readthedocs.io/en/latest/ZFS/).
382
+
383
+
=== "Container DNS"
384
+
AppJail copies `DEFAULT_RESOLV_CONF` to the jail's `resolv.conf(5)` file, whose default value is `/etc/resolv.conf`. Since this file can be modified by many programs, it is recommended that you configure a custom `resolv.conf(5)` file in a more stable location, such as `/usr/local/etc/appjail/resolv.conf`.
AppJail can be integrated with a third-party DNS server, and we can configure that server to read a `hosts(5)` file modified by `appjail-dns(8)`. Our first-class citizen is `dns/dnsmasq`, so let’s set it up. Before doing so, keep in mind that our `resolv.conf(5)` must point to an IP address where DNSMasq can receive packets, but the problem is that if we use a dynamic IP address, this can be problematic. To create a more deterministic environment, we’ll create an `if_tap(4)` interface and set a static IP address, so that our jails point to DNSMasq using that IP address.
393
+
394
+
```bash
395
+
sysrc cloned_interfaces="tap0"
396
+
sysrc ifconfig_tap0_name="ajdns"
397
+
sysrc ifconfig_ajdns="inet 172.0.0.1/32"
398
+
service netif cloneup
399
+
service netif start ajdns
400
+
```
401
+
402
+
**/usr/local/etc/appjail/resolv.conf**:
403
+
404
+
```
405
+
nameserver 172.0.0.1
406
+
```
407
+
408
+
Let's configure DNSMasq and `appjail-dns` rc script.
To test our configuration, simply use a jail's name as the hostname and try to resolve it using a tool like `host(1)`.
422
+
423
+
```console
424
+
# host tautulli
425
+
tautulli has address 10.0.0.3
426
+
```
427
+
428
+
If you've created a jail and then run `host(1)` before the `appjail-dns` rc script detects the changes, you may receive an `NXDOMAIN` error. If you don't want to wait, simply restart the `appjail-dns` rc script.
429
+
430
+
```console
431
+
# service appjail-dns restart
432
+
Stopping appjail_dns.
433
+
Waiting for PIDS: 89362.
434
+
AppJail log file (DNS): /var/log/appjail.log
435
+
Starting appjail_dns.
436
+
# host tautulli
437
+
tautulli has address 10.0.0.3
438
+
```
439
+
440
+
!!! note
441
+
Please note that this only works with Virtual Networks.
442
+
443
+
See also: [DNS on AppJail Handbook](https://appjail.readthedocs.io/en/latest/networking/DNS/).
444
+
138
445
---
139
446
140
-
###Interactive Configuration
447
+
## Interactive Configuration
141
448
142
449
<divclass="placeholder-settings-panel"></div>
143
450
144
-
###Next Steps
451
+
## Next Steps
145
452
-[Available Images](../images/index.md) — Full image fleet
0 commit comments