# Updates

Jails have their own userland and installed applications, so updating the Sylve node does not update the operating systems inside its jails. Update each jail with the tool that matches the root filesystem used during creation.

Sylve creates an independent ZFS-backed copy of the selected root filesystem. Existing jails do not change when you update, replace, or delete the original bootstrap or Base/RootFS download.

:::caution[Do not mix FreeBSD update methods]
A jail created from a Sylve FreeBSD bootstrap uses pkgbase and must be updated with `pkg`. A traditional jail created from `base.txz` must be updated with `freebsd-update` from the host. Do not run both methods against the same jail.
:::

:::note[Bootstraps from Sylve versions before v0.3.2]
Those bootstraps have some incorrect ownership and file-flag metadata, fixed in v0.3.2. During the upgrade Sylve clears them so they can be rebuilt cleanly: start each one again from **Bootstrap Jail Bases** when you need it.

Existing jails are not modified by that cleanup, but a jail copied from an older bootstrap keeps that metadata. Checking one is quick — see [Checking a jail from an older bootstrap](#checking-a-jail-from-an-older-bootstrap). If a bootstrap dataset cannot be removed safely, Sylve leaves it marked and tries again on a later startup; see [If a bootstrap cleanup needs attention](#if-a-bootstrap-cleanup-needs-attention).
:::

## Before every update

1. Read the release notes for the packages or FreeBSD release you plan to install.
2. Confirm that the node's FreeBSD version is at least as new as the jail userland. A jail cannot run a newer FreeBSD userland than its host.
3. Create a clearly named [jail snapshot](/guides/node/jails/snapshots/), such as `before-2026-08-updates`.
4. Use a [backup](/guides/node/jails/backups/) as well when the update affects important or irreplaceable data.
5. Plan a restart and a short validation window for the services inside the jail.

A snapshot is the quickest local rollback point, but it is not an independent backup. If you need to roll back, stop the jail first and use the snapshot's **Rollback** action.

## Identify the jail type

The source selected on the **Storage** step during [jail creation](/guides/node/jails/creation/) determines the update method.

| Creation source | Jail type | Update tool |
| --- | --- | --- |
| **Bootstrap**, such as FreeBSD 15.1 Base or Minimal | FreeBSD pkgbase jail | `pkg` from the jail console |
| Extracted FreeBSD `base.txz` selected as a **Base/RootFS download** | Traditional FreeBSD release-set jail | `freebsd-update -j` from the node |
| Linux **Base/RootFS download** | Linux jail | The distribution's package manager |

If the creation source is no longer known, open the jail [Console](/guides/node/jails/console/) and check for Sylve's pkgbase repository:

```sh
test -f /usr/local/etc/pkg/repos/FreeBSD-base.conf && echo "pkgbase repository configured"
```

That file indicates a configured pkgbase repository, but it does not prove that the base packages are registered. For jails created from older Sylve bootstraps, check the package records as described below. Do not create or remove the repository file merely to change update methods.

## Update a FreeBSD pkgbase jail

This is the normal path for FreeBSD jails created from Sylve's built-in **Bootstrap** option. The bootstrap installs the FreeBSD base system as packages, installs `pkg`, and enables a release-specific `FreeBSD-base` repository inside every jail copied from it.

:::caution[Repair missing package records in an existing bootstrap jail]
A bug in older Sylve bootstraps caused the installed-package database to be discarded. Jails created from those bootstraps inherited the FreeBSD base files without their package records. In an affected jail, `pkg info` may show nothing or only application packages installed later.

Open the jail's **Console** and check for registered base packages:

```sh
pkg info -g 'FreeBSD-*'
```

If this lists no FreeBSD packages in a jail created from **Bootstrap**, repair the missing records before following the normal update steps below. [`pkg upgrade`](https://github.com/freebsd/pkg/blob/main/docs/pkg-upgrade.8) works from registered packages; an empty upgrade does not mean that this jail's base system is up to date.

You can repair the existing jail using its original cached package archives. First, take a [Sylve snapshot](/guides/node/jails/snapshots/). Then run these commands as root inside the jail:

```sh
pkg -o AUTOCLEAN=no install pkg
pkg -v
find /var/cache/pkg -maxdepth 1 -type f -name 'FreeBSD-*.pkg' | head
```

This installs or upgrades and registers `pkg` itself while preserving the package cache. The recovery procedure below was verified with **`pkg` 2.8.4**; use that version or newer. Although 2.6.2 accepts `--register-only`, it has a bug involving configuration-file contents in the recovered database. See the [upstream fixes](https://github.com/freebsd/pkg/blob/main/NEWS).

**Stop before registration if `pkg` is still older than 2.8.4, the cache contains no FreeBSD archives, or the archives do not match the installed base.** For a FreeBSD 15.1 jail, use the original 15.1 archives from its bootstrap. Do not clear the cache, substitute newly downloaded packages, or copy another jail's database: those records may not match the files actually installed.

With the required `pkg` version and original matching archives, restore the base-package registrations:

```sh
find /var/cache/pkg -maxdepth 1 -type f -name 'FreeBSD-*.pkg' \
  -exec pkg install --register-only --local-only {} +

pkg info -g 'FreeBSD-*'
```

The registration command records the packages **without extracting their files or running installation scripts**. The `find` command supplies the archive filenames required by [`pkg install --register-only --local-only`](https://github.com/freebsd/pkg/blob/main/docs/pkg-install.8); running those options without filenames only displays usage information.

Confirm that registration finishes successfully and `pkg info` now lists the FreeBSD base packages, then continue with the normal update steps below. Updating Sylve or recreating a reusable bootstrap does not repair package records in jails that already exist.
:::

Open the running jail's **Console**, then inspect the current userland version:

```sh
freebsd-version -u
```

Refresh the repository catalog and install available base-system and application updates:

```sh
pkg update
pkg upgrade
```

Review the proposed package changes before accepting them. When the upgrade finishes, look for configuration files that need attention:

```sh
find /etc -type f \( -name '*.pkgnew' -o -name '*.pkgsave' \) -print
```

`pkg` performs configuration-file merging for pkgbase. A `.pkgnew` file contains a new version that could not be merged automatically, while a `.pkgsave` file preserves the previous local file. Compare and reconcile these files before removing them. Do not run `etcupdate` on a pkgbase jail.

Restart the jail from Sylve, then confirm the version and test its services:

```sh
freebsd-version -u
service -e
```

### Move a pkgbase jail to another minor release

Upgrade the node first and confirm it is running the target FreeBSD release. Inside the jail, open:

```text
/usr/local/etc/pkg/repos/FreeBSD-base.conf
```

Change its release repository to the target minor version. For example, a FreeBSD 15.0 jail uses `base_release_0`, while FreeBSD 15.1 uses `base_release_1`. Then run:

```sh
pkg update -f
pkg upgrade
```

Restart the jail and review `.pkgnew` and `.pkgsave` files again. For a large version jump, especially a new FreeBSD major version, creating a fresh jail on the desired bootstrap and migrating the application data is usually easier to validate and roll back.

:::note[Updating the bootstrap]
Updating packages inside a jail does not update the reusable bootstrap stored under `<pool>/sylve/bootstraps`. Likewise, creating or replacing a bootstrap does not modify existing jails. Create future jails from a bootstrap for the desired release, then update each created jail independently.
:::

## Checking a jail from an older bootstrap

Sylve versions before **v0.3.2** created pkgbase bootstraps without applying all ownership and file-flag metadata. A jail copied from one of those bootstraps may have files owned by `root:wheel` where an account such as `mail` was intended, which can change who can read or write files like the mail spool.

The upgrade clears the affected reusable bootstraps; it does not modify existing jails. Checking a jail takes a minute, and replacing it is usually the easiest fix.

### Check the jail

The jail is a pkgbase candidate when it contains Sylve's pkgbase repository configuration:

```sh
test -f /usr/local/etc/pkg/repos/FreeBSD-base.conf && echo "pkgbase repository configured"
```

Open the jail's **Console** and check representative metadata:

```sh
stat -f '%Sp %u:%g %Su:%Sg flags=%Sf %N' \
  /var/mail \
  /var/spool/dma \
  /usr/libexec/dma \
  /usr/libexec/dma-mbox-create \
  /var/empty
```

With base packages registered and `pkg` 2.4.0 or newer, this audit gives a fuller picture:

```sh
pkg info -g 'FreeBSD-*'
pkg check -m -a
```

A clean audit is a useful signal, but not a complete one: a package database written before `pkg` 2.4 does not contain the per-path metadata, and a newer `pkg` skips what is missing. Use the `stat` checks alongside it — the paths above are examples, not the full set.

Opening an old package database with a newer `pkg` can upgrade it in place. Take a [snapshot](/guides/node/jails/snapshots/) first if you want to keep the original.

:::note[Version requirements]
`pkg check -m` needs **pkg 2.4.0 or newer**. The register-only recovery in the pkgbase update section needs **pkg 2.8.4 or newer**.
:::

### Option A: replace the jail

This is the simplest and most predictable fix:

1. Take a snapshot and a separate [backup](/guides/node/jails/backups/) of important application data.
2. Rebuild the bootstrap with the current Sylve version.
3. Create a replacement jail from it.
4. Reinstall the application and restore its configuration and data.
5. Check the replacement before deleting the old jail.

This is quick for disposable or lightly configured jails. For a stateful service, plan where the data lives and schedule a short cutover.

### Option B: in-place reinstall (expert-only)

Sylve does not automate or support this; it is described only to explain why replacing the jail is recommended.

- It must run from the node against the stopped jail root, because `pkg` uses the host's repository configuration when it targets a root directory, not the jail's own config files.
- It restores ownership, but `pkg` skips attribute updates when ownership and mode already match, so declared flags can stay missing. In testing, `/var/empty` kept its mode and ownership but stayed without `schg` after a forced reinstall.
- It can replace base files, run package scripts, and leave `.pkgnew` or `.pkgsave` files, and it needs the exact registered versions to be available.

If you want to try it, stop the jail, take a snapshot, and force-reinstall the exact registered versions with `INSTALL_AS_USER` absent — always targeting the jail root and its package database instead of the host. Verify with both audits pointed at the same target, because running them plainly on the node audits the host:

```sh
pkg --rootdir <jail-root> -o PKG_DBDIR=<jail-root>/var/db/pkg check -m -a
pkg --rootdir <jail-root> -o PKG_DBDIR=<jail-root>/var/db/pkg check -s -a
```

If anything still reports `[fflags]`, treat it as unresolved and use [Option A](#option-a-replace-the-jail) instead.

### Templates, snapshots, backups, and replicas

Copies made before the fix keep the same metadata. A template captured from an affected jail can pass it on to new jails, and restoring a snapshot, backup, or replica brings it back. If you use one of those later, run the checks above on the resulting jail. Rebuilding a template from a checked jail is the cleanest option.

### If a bootstrap cleanup needs attention

If a bootstrap dataset cannot be removed safely, Sylve leaves it marked, starts normally, and tries again on the next startup. Two situations need a manual look. In both, check the dataset before deleting anything: a missing record or a clone means Sylve cannot be sure the data is disposable.

- **A clone depends on it:** Sylve leaves the bootstrap in place because removing it could affect data it does not own, and the UI/API will not resolve the clone for you. Inspect it with:

  ```sh
  zfs get -H -r -t snapshot -o name,value clones <bootstrap-dataset>
  ```

  The clone may contain data, and it has the same metadata as the bootstrap it came from. Decide whether to keep, copy or promote, or remove it, then restart Sylve to retry. Avoid `zfs destroy -R`.
- **A dataset has no record:** a dataset under `<pool>/sylve/bootstraps` with no Sylve record may be something you created or still need. It does not affect the upgrade, but Sylve will not create that bootstrap again until the dataset is resolved. Look at its contents, children, snapshots, and clones first:

  ```sh
  zfs list -r <bootstrap-dataset>
  zfs list -t snapshot -r <bootstrap-dataset>
  zfs get -H -r -t snapshot -o name,value clones <bootstrap-dataset>
  ```

  If nothing there is needed, use Sylve's **Delete** action for that exact bootstrap — it removes the dataset, its children, and its snapshots — then create the bootstrap again. Use that action rather than a raw `zfs destroy`, and handle any clone first, since a clone blocks deletion.

## Update a traditional FreeBSD `base.txz` jail

A jail created from an extracted `base.txz` is not a pkgbase installation. FreeBSD requires these jails to be updated from the host because protected file flags can prevent a complete base-system update from inside the jail.

Open the node's [Terminal](/guides/node/terminal/) and list the running jails:

```sh
jls name host.hostname path
```

Sylve uses an internal five-letter runtime name. Identify the correct entry by matching its hostname or the root path ending in the jail's CTID. Use that value as `<runtime-name>` below.

Apply security and errata updates for the jail's current release:

```sh
freebsd-update -j <runtime-name> fetch install
```

Restart the jail from Sylve, then open its console and verify the userland:

```sh
freebsd-version -u
```

Application packages are separate from the FreeBSD base system. Update them from the jail console:

```sh
pkg update
pkg upgrade
```

### Move a traditional jail to another FreeBSD release

Upgrade and reboot the node first. The node must run the target release or a newer compatible release. With the jail running, execute from the node terminal:

```sh
freebsd-update -j <runtime-name> -r 15.1-RELEASE upgrade
freebsd-update -j <runtime-name> install
```

Restart the jail from Sylve, then complete the second installation pass from the node:

```sh
freebsd-update -j <runtime-name> install
```

Restart the jail again. The two installation passes are intentional. For an upgrade across FreeBSD major versions, also reinstall the jail's application packages for the new ABI:

```sh
pkg -j <runtime-name> upgrade -f
```

For a large release jump, consider creating a new jail from the target release and migrating the application configuration and data. This provides a cleaner rollback boundary than modifying a long-lived jail in place.

## Update a Linux jail

Open the jail **Console** and use the package manager supplied by its distribution. Common examples include:

### Debian or Ubuntu

```sh
apt update
apt full-upgrade
```

### Alpine Linux

```sh
apk update
apk upgrade
```

### Rocky Linux or another DNF-based distribution

```sh
dnf upgrade
```

Follow that distribution's documented procedure for major release upgrades. Linux jails use the FreeBSD Linux compatibility layer, so verify both application behavior and compatibility after significant userland changes.

## Validate the update

After restarting the jail:

1. Confirm that it returns to the **Active** state on its [Summary](/guides/node/jails/summary/) page.
2. Review the jail logs for startup failures.
3. Open the **Console** and confirm the operating-system version.
4. Test the application, network listeners, mounted storage, and scheduled services.
5. Keep the pre-update snapshot until the workload has completed a meaningful validation period.

If validation fails, collect the relevant logs before rolling back. A snapshot rollback discards every filesystem change made after that recovery point, including application data written after the update.

For the underlying FreeBSD procedures and their current limitations, see the [FreeBSD Handbook jail-upgrading chapter](https://docs.freebsd.org/en/books/handbook/jails/#jail-upgrading).