{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreibfcxbpgxcozqvi3y73nb3nrhyj4e4rkj6d2s5uuj2gh4iddf42vu",
"uri": "at://did:plc:5y2ps7xhcqmc2d63b73ui72s/app.bsky.feed.post/3mp2iocjgdnj2"
},
"description": "Learn how to build fully automated Kali Linux 2026.1 Proxmox templates using Packer, cloud-init and a Debian preseed file. This guide covers the final working configuration after extensive troubleshooting and testing.",
"path": "/building-kali-2026-1-images-with-packer-proxmox-and-cloud-init/",
"publishedAt": "2026-06-24T18:00:55.000Z",
"site": "https://blog.php-systems.com",
"textContent": "Creating automated Kali Linux templates for Proxmox should be straightforward, but in practice I spent far more time than expected getting everything working correctly together.\n\nThe combination of:\n\n * Packer\n * Proxmox\n * Kali Linux 2026.1\n * Debian Preseed\n * Cloud-Init\n * Proxmox Templates\n\n\n\nrequired a surprising amount of trial and error before arriving at a reliable build process.\n\nThis article documents the final working configuration that successfully builds a Kali Linux 2026.1 template ready for deployment through Proxmox cloud-init.\n\n## Why Build Kali Templates with Packer?\n\nAutomating Kali image creation provides several advantages:\n\n * Consistent deployments\n * Repeatable infrastructure builds\n * Faster lab provisioning\n * Version-controlled templates\n * Cloud-init integration\n * Easy integration with Terraform and Ansible\n\n\n\nInstead of manually installing Kali every time a new VM is required, Packer can automatically build a clean template that can be cloned repeatedly.\n\n## Environment\n\nMy build environment consists of:\n\nComponent| Version\n---|---\nProxmox VE| 8.x\nPacker| 1.x\nKali Linux| 2026.1\nCloud-Init| Current\nDebian Installer| Preseed Automated\n\nThe Packer build machine hosts a temporary HTTP server which serves the preseed and cloud-init configuration files during installation.\n\n* * *\n\n# Directory Structure\n\nMy project structure looks similar to the following:\n\n\n kali-2026.1/\n ├── kali-2026-1.pkr.hcl\n ├── credentials.pkr.hcl\n ├── http/\n │ ├── preseed.cfg\n │ ├── meta-data\n │ └── user-data\n └── files/\n └── 99-pve.cfg\n\nThe credentials file contains API tokens and other secrets and is intentionally excluded from source control.\n\n* * *\n\n# Packer Configuration\n\nThe following is the final working Packer configuration used to build the Kali 2026.1 template..\n\n\n # Kali 2026-1\n # ---\n # Packer Template to create a Kali 2026-1 on Proxmox\n\n # Variable Definitions\n variable \"proxmox_api_url\" {\n type = string\n }\n\n variable \"proxmox_api_token_id\" {\n type = string\n }\n\n variable \"proxmox_api_token_secret\" {\n type = string\n sensitive = true\n }\n\n variable \"http_ip\" {\n type = string\n default = \"<your packer server ip>\"\n description = \"IP address to bind the HTTP server for cloud-init\"\n }\n\n packer {\n required_plugins {\n name = {\n version = \"~> 1\"\n source = \"github.com/hashicorp/proxmox\"\n }\n }\n }\n\n source \"proxmox-iso\" \"kali-2026-1\" {\n\n proxmox_url = \"${var.proxmox_api_url}\"\n username = \"${var.proxmox_api_token_id}\"\n token = \"${var.proxmox_api_token_secret}\"\n\n node = \"pve-01\"\n vm_id = \"8510\"\n vm_name = \"kali-2026-1\"\n template_description = \"Kali 2026.1\"\n\n iso_file = \"local:iso/kali-linux-2026.1-installer-amd64.iso\"\n iso_storage_pool = \"local\"\n\n template_name = \"packer-kali20261\"\n\n qemu_agent = true\n\n scsi_controller = \"virtio-scsi-pci\"\n\n disks {\n disk_size = \"20G\"\n format = \"raw\"\n storage_pool = \"local-lvm\"\n type = \"virtio\"\n }\n\n cores = \"4\"\n cpu_type = \"host\"\n\n memory = \"4096\"\n\n network_adapters {\n model = \"virtio\"\n bridge = \"vmbr0\"\n firewall = \"false\"\n }\n\n cloud_init = true\n cloud_init_storage_pool = \"local-lvm\"\n\n boot_command = [\n \"<esc><wait>\",\n \"install preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg <wait>\",\n \"debian-installer=en_GB auto locale=en_GB kbd-chooser/method=gb <wait>\",\n \"netcfg/get_hostname=kali netcfg/get_domain=local fb=false <wait>\",\n \"debconf/frontend=noninteractive console-setup/ask_detect=false <wait>\",\n \"console-keymaps-at/keymap=gb keyboard-configuration/xkb-keymap=gb <wait>\",\n \"<enter><wait>\"\n ]\n\n boot = \"c\"\n boot_wait = \"5s\"\n\n http_directory = \"http\"\n http_bind_address = \"${var.http_ip}\"\n\n http_port_min = 8900\n http_port_max = 8999\n\n ssh_username = \"ansible\"\n\n ssh_private_key_file = \"/nsm/ansible/keys/juniper-hosts.key\"\n\n ssh_timeout = \"40m\"\n }\n\n build {\n\n name = \"kali-2026-1\"\n sources = [\"proxmox-iso.kali-2026-1\"]\n\n provisioner \"shell\" {\n inline = [\n \"sudo rm /etc/ssh/ssh_host_*\",\n \"sudo truncate -s 0 /etc/machine-id\",\n \"sudo apt -y autoremove --purge\",\n \"sudo apt -y clean\",\n \"sudo apt -y autoclean\",\n \"sudo cloud-init clean\",\n \"sudo rm -f /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg\",\n \"sudo rm -f /etc/netplan/00-installer-config.yaml\",\n \"sudo sync\"\n ]\n }\n }\n\n* * *\n\n# Preseed Configuration\n\nThe installer is driven entirely through a Debian preseed file.\n\n**Placeholder: Insert preseed.cfg here**\n\n\n ### General Config\n\n d-i debian-installer/language string en\n d-i debian-installer/country string GB\n d-i debian-installer/locale string en_GB.UTF-8\n d-i clock-setup/utc boolean true\n d-i time/zone string Europe/London\n\n ### Keyboard Config\n\n d-i console-setup/ask_detect boolean false\n d-i keyboard-configuration/xkb-keymap select gb\n\n ### Network Config\n\n d-i netcfg/choose_interface select auto\n d-i netcfg/get_hostname string kali-cleanroom\n d-i netcfg/get_domain string local.lan\n d-i netcfg/hostname string kali-cleanroom\n\n ### Root Account Setup\n\n d-i passwd/root-login boolean true\n d-i passwd/make_user boolean false\n d-i passwd/root-password password RootSuperSecureTemp!\n d-i passwd/root-password-again password RootSuperSecureTemp!\n d-i user-setup/encrypt-home boolean false\n\n ### Ansible User Setup\n\n d-i passwd/make-user boolean true\n d-i passwd/user-fullname string Ansible user\n d-i passwd/username string ansible\n d-i passwd/user-password password AnsibleSuperSecureTemp!\n d-i passwd/user-password-again password AnsibleSuperSecureTemp!\n d-i user-setup/encrypt-home boolean false\n d-i user-setup/allow-password-weak boolean false\n d-i passwd/user-default-groups string audo cdrom video admin sudo\n\n ### Partitioning\n\n d-i partman-auto/method string lvm\n d-i partman-lvm/device_remove_lvm boolean true\n d-i partman-md/device_remove_md boolean true\n d-i partman-lvm/confirm boolean true\n d-i partman-lvm/confirm_nooverwrite boolean true\n d-i partman-auto-lvm/guided_size string max\n d-i partman-auto/choose_recipe select atomic\n d-i partman-partitioning/confirm_write_new_label boolean true\n d-i partman/choose_partition select finish\n d-i partman/confirm_nooverwrite boolean true\n\n ### Mirror Settings\n\n d-i mirror/country string manual\n d-i mirror/http/hostname string http.kali.org\n d-i mirror/http/directory string /\n d-i mirror/http/proxy string\n\n ### APT Setup\n\n d-i apt-setup/use_mirror boolean true\n\n ### Desktop type\n tasksel tasksel/first multiselect standard,core,meta-default\n #tasksel tasksel/first multiselect standard,core,desktop-xfce,meta-default\n d-i pkgsel/include string openssh-server build-essential sudo cloud-init\n d-i pkgsel/install-language-support boolean false\n d-i pkgsel/update-policy select none\n d-i pkgsel/upgrade select full-upgrade\n\n ### Bootloader\n\n d-i grub-installer/only_debian boolean true\n #d-i grub-installer/password password GrubSecret!\n #d-i grub-installer/password-again password GrubSecret!\n d-i grub-installer/bootdev string default\n\n\n ### Finishing Up\n\n d-i preseed/late_command string \\\n echo 'ansible ALL=(ALL) NOPASSWD: ALL' > /target/etc/sudoers.d/ansible ; \\\n in-target chmod 440 /etc/sudoers.d/ansible ; \\\n in-target apt install -y cloud-init ; \\\n in-target /bin/sh -c \"echo 'datasource_list: [ ConfigDrive, NoCloud ]' > /etc/cloud/cloud.cfg.d/99_datasource.cfg\"; \\\n in-target systemctl enable ssh.service ; \\\n in-target touch /etc/cloud/cloud-init.enabled; \\\n in-target systemctl daemon-reload; \\\n in-target systemctl unmask cloud-init-local.service cloud-init.service cloud-config.service cloud-final.service; \\\n in-target systemctl enable cloud-init-local.service cloud-init.service cloud-config.service cloud-final.service; \\\n in-target mkdir -p /home/ansible/.ssh; \\\n in-target /bin/sh -c \"echo 'ssh-rsa AAAAB...' >> /home/ansible/.ssh/authorized_keys\"; \\\n in-target chown -R ansible:ansible /home/ansible/.ssh/; \\\n in-target chmod 644 /home/ansible/.ssh/authorized_keys; \\\n in-target chmod 700 /home/ansible/.ssh/; \\\n in-target cloud-init clean --logs --seed; \\\n in-target sh -c \"truncate -s 0 /etc/machine-id\";\n\n\n d-i finish-install/reboot_in_progress note\n\nA properly configured preseed file is critical because it handles:\n\n * Disk partitioning\n * User creation\n * Package selection\n * SSH configuration\n * Cloud-init installation\n * qemu-guest-agent installation\n\n\n\n* * *\n\n# Cloud-Init User Data\n\nCloud-init is responsible for configuring cloned instances after deployment.\n\n**Placeholder: Insert user-data file here**\n\n\n #cloud-config\n autoinstall:\n version: 1\n locale: en_GB\n keyboard:\n layout: gb\n ssh:\n install-server: true\n allow-pw: false\n disable_root: true\n ssh_quiet_keygen: true\n allow_public_ssh_keys: true\n packages:\n - qemu-guest-agent\n - sudo\n storage:\n layout:\n name: direct\n swap:\n size: 0\n user-data:\n package_upgrade: true\n timezone: Europe/London\n users:\n - name: ansible\n groups: [adm, sudo]\n lock-passwd: false\n sudo: ALL=(ALL) NOPASSWD:ALL\n shell: /bin/bash\n # passwd: your-password\n # - or -\n ssh_authorized_keys:\n - ssh-rsa AAAAB...\n\n* * *\n\n# Cloud-Init Meta Data\n\nCloud-init metadata provides instance identification information.\n\n**This file is a just empty and for future use.**\n\n* * *\n\n# Proxmox Cloud-Init Configuration\n\nSome environments also require a custom cloud-init configuration file.\n\n**Placeholder: Insert 99-pve.cfg here**\n\n\n datasource_list: [ConfigDrive, NoCloud]\n\n* * *\n\n# Credentials File\n\nThe build process also relies on a separate credentials file containing:\n\n * Proxmox API URL\n * Token ID\n * Token Secret\n * Environment-specific variables\n\n\n\nThis file is intentionally excluded from this article and should never be committed to source control.\n\nExample:\n\n\n proxmox_api_url = \"https://pve-01:8006/api2/json\" # Your Proxmox IP Address\n proxmox_api_token_id = \"root@pam!packer\" # API Token ID\n proxmox_api_token_secret = \"your packer token\"\n\n* * *\n\n# Template Cleanup for Cloud-Init\n\nOne of the most important parts of the build process happens after installation.\n\nBefore converting the VM into a template, the build removes:\n\n\n rm /etc/ssh/ssh_host_*\n truncate -s 0 /etc/machine-id\n cloud-init clean\n\nThis ensures every cloned VM receives:\n\n * Unique SSH host keys\n * Unique machine ID\n * Fresh cloud-init execution\n\n\n\nWithout these cleanup steps, cloned systems can exhibit strange behaviour and duplicate identities.\n\n* * *\n\n# Lessons Learned\n\nThe biggest challenges during this build were:\n\n 1. Getting Kali's installer to work reliably with preseed.\n 2. Ensuring cloud-init ran correctly after cloning.\n 3. Removing installer-generated network configuration.\n 4. Cleaning machine identity information before template conversion.\n 5. Correctly serving preseed files through Packer's temporary HTTP server.\n 6. Matching storage locations between ISO, VM disks and cloud-init disks.\n\n\n\nMost online examples are based on Ubuntu, while Kali introduces several small differences that can make troubleshooting frustrating.\n\n* * *\n\n# Final Thoughts\n\nAfter a significant amount of experimentation, this build process now consistently produces a Kali Linux 2026.1 Proxmox template that works correctly with cloud-init and can be deployed repeatedly without manual intervention.\n\nThe result is a fully automated workflow where Packer creates the template, Proxmox stores it, and cloud-init customises every cloned VM during first boot.\n\nFor anyone building Kali templates in a home lab, penetration testing environment or security research platform, investing the time in a reliable Packer workflow pays dividends very quickly.",
"title": "Building Kali 2026.1 Images with Packer, Proxmox and Cloud-Init",
"updatedAt": "2026-06-24T18:00:55.597Z"
}