Skip to main content

HashiCorp Packer with Virtualbox (Preseed)

TST, Hong Kong

I want to build a Ubuntu VirtualBox image using HashiCorp Packer. We do this by creating a JSON template that Packer will use to create the image. The template can then be used to create identical machines on multiple platforms. The building process takes the installation ISO file of your chosen OS and uses it to create a full-blown installation of the OS by mounting the ISO, booting off it, dealing with the OS installer initial UI, and proceeding with an unattended installation. If everything goes well, we then have a functional, bootable VM image.

The unattended installation is made possible by a set of boot_command 's. One of these commands will instruct the Ubuntu installer to fetch a preconfiguration file (preseed.cfg) that will automatically provide answers to the installer prompts.

I will create this template file and the Ubuntu preconfiguration file inside a folder named packer/http. This will be the directory Packer will make available over http to the VM while it is created.

Template File

Let’s name the template ubuntu_64.json:

  "builders": [
      "boot_command": [
        "/install/vmlinuz noapic ",
        "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ubuntu_64_preseed.cfg ",
        "debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
        "hostname={{user hostname}} ",
        "fb=false debconf/frontend=noninteractive ",
        "keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
        "keyboard-configuration/variant=USA console-setup/ask_detect=false ",
        "initrd=/install/initrd.gz -- <enter>"
      "disk_size": 10000,
      "guest_os_type": "Ubuntu_64",
      "http_directory": "http",
      "http_port_max": 9001,
      "http_port_min": 9001,
      "iso_checksum": "md5:769474248a3897f4865817446f9a4a53",
      "iso_url": "",
      "shutdown_command": "echo {{user ssh_pass}} | sudo -S shutdown -P now",
      "ssh_password": "{{user ssh_pass}}",
      "ssh_timeout": "20m",
      "ssh_username": "{{user ssh_name}}",
      "type": "virtualbox-iso",
      "vboxmanage": [["modifyvm", "{{.Name}}", "--vram", "32"]]
  "variables": {
    "hostname": "packer-ubuntu-12",
    "ssh_name": "ubuntu",
    "ssh_pass": "ubuntu"

When I validated my first version of this template I hit a deprecation warning and was asked to run the FIX command. This printed out the template shown above which validated in Packer version 1.6.4:

packer validate ubuntu_64.json
packer fix ubuntu_64.json

Preseed File

Now we need the ubuntu_64_preseed.cfg file that is used to preconfigure the installer:

# Some inspiration:
# *
# *

# English plx
d-i debian-installer/language string en
d-i debian-installer/locale string en_US.UTF-8
d-i localechooser/preferred-locale string en_US.UTF-8
d-i localechooser/supported-locales en_US.UTF-8

# Including keyboards
d-i console-setup/ask_detect boolean false
d-i keyboard-configuration/layout select USA
d-i keyboard-configuration/variant select USA
d-i keyboard-configuration/modelcode string pc105

# Just roll with it
d-i netcfg/get_hostname string packer-test
d-i netcfg/get_domain string packer-test

d-i time/zone string UTC
d-i clock-setup/utc-auto boolean true
d-i clock-setup/utc boolean true

# Choices: Dialog, Readline, Gnome, Kde, Editor, Noninteractive
d-i debconf debconf/frontend select Noninteractive

d-i pkgsel/install-language-support boolean false
tasksel tasksel/first multiselect standard, ubuntu-server

# Stuck between a rock and a HDD place
d-i partman-auto/method string lvm
d-i partman-lvm/confirm boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-auto/choose_recipe select atomic

d-i partman/confirm_write_new_label boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true

# Write the changes to disks and configure LVM?
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-auto-lvm/guided_size string max

# No proxy, plx
d-i mirror/http/proxy string

# Default user, change
d-i passwd/user-fullname string ubuntu
d-i passwd/username string ubuntu
d-i passwd/user-password password ubuntu
d-i passwd/user-password-again password ubuntu
d-i user-setup/encrypt-home boolean false
d-i user-setup/allow-password-weak boolean true

# No language support packages.
d-i	pkgsel/install-language-support boolean false

# Individual additional packages to install
d-i pkgsel/include string build-essential ssh

#For the update
d-i pkgsel/update-policy select none

# Whether to upgrade packages after debootstrap.
# Allowed values: none, safe-upgrade, full-upgrade
d-i pkgsel/upgrade select safe-upgrade

# Go grub, go!
d-i grub-installer/only_debian boolean true

d-i finish-install/reboot_in_progress note


Now I can start the build with the following command:

Packer Virtualbox

packer build ./http/ubuntu_64.json


==> Builds finished. The artifacts of successful builds are:
--> virtualbox-iso: VM files in directory: output-virtualbox-iso

VERY IMPORTANT: Both files - the ubuntu_64.json and ubuntu_64_preseed.cfg (only the preseed) are inside the http folder and I call packer from the folder that contains this folder. If you need to change this structure, change the following line inside the template file "http_directory": "http".