The Packer Plugin for Sylve creates machine images for Sylve — a lightweight, open-source FreeBSD management platform for Bhyve VMs, FreeBSD Jails, and ZFS storage.
The plugin communicates with the Sylve REST API (default base URL https://<host>:8181)
to provision, configure, and snapshot virtual machines directly on a FreeBSD host.
-
sylve-iso— Creates a Bhyve VM, boots it from an ISO image that is downloaded via the Sylve download manager, runs provisioners over SSH, and produces an artifact from the resulting VM state. Use this builder to create a new image from scratch. -
sylve-vm(planned) — Starts from an existing Sylve VM snapshot instead of booting from ISO, runs provisioners, and produces an updated artifact. -
sylve-jail(planned) — Creates a FreeBSD Jail image via the Sylve API.
sylve(planned) — Configures a running Sylve guest.
sylve(planned) — Processes build artifacts after the Packer build completes.
sylve(planned) — Queries existing Sylve resources for use in Packer templates.
Sylve:
A running Sylve instance on FreeBSD 15.0 or later is required. The plugin
connects to the Sylve REST API over HTTPS (default port 8181). Sylve ships with a
self-signed TLS certificate; set tls_skip_verify = true unless you have a
trusted certificate installed.
Sylve virtual switch (DHCP required):
The plugin discovers the provisioned VM's IP address by polling the Sylve DHCP lease
API. The virtual switch named in switch_name (or SYLVE_SWITCH) must have DHCP
enabled — i.e. the switch bridge must have an IP address assigned and Sylve's built-in
dnsmasq DHCP server must be running on it. Without DHCP the plugin cannot determine the
VM's IP and the build will time out waiting for an SSH connection.
To verify the switch is configured correctly, check the Sylve web UI or API:
the switch should have an IP/subnet set and DHCP enabled. The VM must be attached
to this switch at build time (set switch_name accordingly).
Go:
- Go 1.26.1 or later is required to build the plugin from source.
The GitHub repository and the Go module in go.mod use the long form
github.com/xoro/packer-plugin-sylve, as required for Packer plugins hosted on GitHub.
In Packer templates and CLI commands, the plugin source is the short form
github.com/xoro/sylve. Packer resolves that to the packer-plugin-sylve repository and
does not allow the packer-plugin- prefix in the source string. The same split appears
in other plugins (for example, VMware uses github.com/vmware/vmware with repository
packer-plugin-vmware). See the
Packer plugin installation documentation for details.
Include the following in your Packer template to automatically install the plugin when
you run packer init.
packer {
required_version = ">= 1.15.0"
required_plugins {
sylve = {
version = ">= 0.1.0"
source = "github.com/xoro/sylve"
}
}
}For more information, refer to the Packer documentation.
Install the plugin using the packer plugins install command:
packer plugins install github.com/xoro/sylveClone the repository and run make build:
git clone https://github.com/xoro/packer-plugin-sylve.git
cd packer-plugin-sylve
make buildThe packer-plugin-sylve binary is created in the repository root. To install it into
Packer's plugin directory, refer to the Packer
plugin installation documentation.
For development on a FreeBSD host, make dev builds and installs the plugin directly
into the Packer plugin path.
The sylve-iso builder supports two authentication methods:
- Token-based: set
sylve_tokenor theSYLVE_TOKENenvironment variable with a pre-issued Bearer token. - Login-based: set
sylve_user/SYLVE_USERandsylve_password/SYLVE_PASSWORD. The builder logs in at the start of the build and logs out when it finishes. Setsylve_auth_typeto"pam"for PAM authentication (default is"sylve"for native database accounts).
packer {
required_plugins {
sylve = {
version = ">= 0.1.0"
source = "github.com/xoro/sylve"
}
}
}
source "sylve-iso" "freebsd" {
sylve_url = "https://192.168.1.10:8181"
sylve_token = env("SYLVE_TOKEN")
iso_download_url = "https://download.freebsd.org/releases/amd64/amd64/ISO-IMAGES/15.0/FreeBSD-15.0-RELEASE-amd64-dvd1.iso"
vm_name = "freebsd-packer"
cpu_cores = 2
ram = 2048
storage_size_mb = 20480
switch_name = "packer-switch"
boot_wait = "15s"
boot_command = [
"<enter>",
]
ssh_username = "root"
ssh_password = "sylve-example"
ssh_timeout = "30m"
shutdown_command = "/sbin/poweroff"
restart_after_install = false
destroy = true
}
build {
sources = ["source.sylve-iso.freebsd"]
provisioner "shell" {
inline = ["echo 'Build complete'"]
}
}| Option | Required | Default | Description |
|---|---|---|---|
sylve_url |
https://localhost:8181 |
Base URL of the Sylve instance | |
sylve_token |
one of | Pre-issued Bearer token | |
sylve_user + sylve_password |
one of | Login credentials | |
iso_download_url |
yes | URL passed to Sylve's download manager | |
switch_name |
yes (or SYLVE_SWITCH) |
Name of a Sylve virtual switch with DHCP enabled (dnsmasq). The VM's IP is discovered via the Sylve DHCP lease API — the switch must have an IP/subnet and DHCP active. | |
cpu_cores |
2 |
Number of vCPU cores | |
ram |
1024 |
Memory in MiB | |
storage_size_mb |
65536 |
Install disk size in MiB | |
storage_pool |
ZFS pool name on the Sylve host. Falls back to SYLVE_POOL. Plugin picks the first available pool when empty. |
||
loader |
uefi |
Firmware: uefi or bios |
|
boot_wait |
10s |
Wait before sending VNC boot commands | |
http_directory |
Directory of files to serve over HTTP to the guest (accessible as {{ .HTTPIP }}:{{ .HTTPPort }} in boot_command) |
||
http_content |
Map of URL path → content string served over HTTP; alternative to http_directory |
||
http_port_min |
8000 |
Lower bound of the port range for the built-in HTTP server | |
http_port_max |
9000 |
Upper bound of the port range for the built-in HTTP server | |
http_bind_address |
IP address the HTTP server listens on; defaults to all interfaces | ||
restart_after_install |
false |
Force-stop the installer VM, disable the ISO storage, and restart before provisioning. Set to true for OS installers that reboot into the CD again on Bhyve auto-restart (e.g. Alpine). Leave false (default) for installers that update UEFI NVRAM and boot the installed disk on the next start (e.g. OpenBSD, FreeBSD, Debian). |
|
destroy |
false |
Delete the VM after a successful build | |
tls_skip_verify |
true |
Skip TLS certificate verification | |
sylve_api_login_timeout |
2m |
Retry window for the initial login when the Sylve API is unreachable. Ignored when sylve_token is set. Falls back to SYLVE_API_LOGIN_TIMEOUT. |
# Build
make build
# Run unit tests
go test ./...
# Run acceptance tests (requires a live Sylve instance)
PACKER_ACC=1 SYLVE_URL=https://host:8181 SYLVE_TOKEN=<token> go test -count 1 -v ./... -timeout=120m
# Format code
./bin/format_files.sh
# Run linters
./bin/run_linter_checks.sh
# Run security scanners
./bin/run_security_scanners.sh| Variable | Equivalent HCL Field / Purpose |
|---|---|
SYLVE_HOST |
Sets sylve_url as https://<host>:8181 when sylve_url is not set in HCL. |
SYLVE_TOKEN |
sylve_token |
SYLVE_USER |
sylve_user |
SYLVE_PASSWORD |
sylve_password |
SYLVE_AUTH_TYPE |
sylve_auth_type — "sylve" (default) or "pam" |
SYLVE_API_LOGIN_TIMEOUT |
sylve_api_login_timeout — login retry window (default 2m) |
SYLVE_SWITCH |
switch_name |
SYLVE_POOL |
storage_pool |
SYLVE_SSH_PROXY |
Set to false or 0 to disable automatic SSH bastion configuration (opt-out; on by default when Sylve host is remote). |
SYLVE_SSH_PROXY_KEY |
Path to an SSH private key file to use for the automatic bastion connection. |
Contributions are welcome. Please open an issue or pull request on the GitHub repository.
Copyright (c) 2026, Timo Pallach.
Licensed under the BSD 2-Clause License.