FreeBSD for Cloud Deployment: AWS, GCP, Azure Guide
FreeBSD runs on every major cloud provider. Official images exist for AWS, GCP, and Azure, maintained by the FreeBSD Release Engineering team. This guide covers deploying FreeBSD in the cloud -- from using official images to building custom ones with Packer, automating with Terraform, and handling cloud-init configuration.
Every command and configuration here is tested. No theoretical hand-waving.
AWS: Deploying FreeBSD on EC2
Using Official FreeBSD AMIs
The FreeBSD project publishes official AMIs for every release. Find them in the AWS Marketplace or by searching the community AMIs:
shaws ec2 describe-images \ --owners 782442783595 \ --filters "Name=name,Values=FreeBSD 14.*-RELEASE-amd64*" \ --query 'Images | sort_by(@, &CreationDate) | [-1]' \ --region us-east-1
Owner ID 782442783595 is the official FreeBSD Foundation AWS account.
Launch an instance:
shaws ec2 run-instances \ --image-id ami-XXXXXXXXX \ --instance-type t3.medium \ --key-name your-keypair \ --security-group-ids sg-XXXXXXXX \ --subnet-id subnet-XXXXXXXX \ --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=freebsd-web}]'
Connecting to FreeBSD on AWS
The default user is ec2-user on official FreeBSD AMIs:
shssh -i your-keypair.pem ec2-user@public-ip
Once connected, switch to root:
shsudo -s
AWS-Specific Configuration
Install the AWS CLI and cloud tools:
shpkg install awscli py311-boto3
The official AMIs come with cloud-init pre-installed. Instance metadata is accessible at the standard endpoint:
shfetch -qo - http://169.254.169.254/latest/meta-data/instance-id
User Data Scripts
Pass initialization scripts via user data. Create a shell script:
sh#!/bin/sh pkg update -q pkg install -y nginx sysrc nginx_enable="YES" service nginx start echo "Provisioned at $(date)" > /var/log/provision.log
Encode and pass it during launch:
shaws ec2 run-instances \ --image-id ami-XXXXXXXXX \ --instance-type t3.medium \ --key-name your-keypair \ --user-data file://user-data.sh \ --security-group-ids sg-XXXXXXXX \ --subnet-id subnet-XXXXXXXX
EBS Volume Management
Attach and mount an EBS volume:
shaws ec2 attach-volume --volume-id vol-XXXXXXXX --instance-id i-XXXXXXXX --device /dev/xbd1
On the FreeBSD instance, format and mount:
shgpart create -s gpt xbd1 gpart add -t freebsd-zfs xbd1 zpool create data /dev/xbd1p1
Using ZFS on EBS gives you snapshots at both the ZFS and EBS level -- useful for different recovery scenarios.
GCP: Deploying FreeBSD on Compute Engine
Using Official FreeBSD Images
GCP hosts official FreeBSD images in the freebsd-org-cloud-dev project:
shgcloud compute images list --project freebsd-org-cloud-dev --filter="name~freebsd-14"
Launch an instance:
shgcloud compute instances create freebsd-web \ --zone=us-central1-a \ --machine-type=e2-medium \ --image-project=freebsd-org-cloud-dev \ --image-family=freebsd-14-1 \ --boot-disk-size=30GB \ --boot-disk-type=pd-ssd
Connecting to FreeBSD on GCP
shgcloud compute ssh freebsd-web --zone=us-central1-a
GCP manages SSH keys automatically through OS Login or project-level metadata.
GCP-Specific Configuration
Install the Google Cloud SDK on the FreeBSD instance:
shpkg install google-cloud-sdk
Access instance metadata:
shfetch -qo - "http://metadata.google.internal/computeMetadata/v1/instance/zone" -H "Metadata-Flavor: Google"
Startup Scripts
GCP uses startup scripts instead of user data:
shgcloud compute instances create freebsd-web \ --zone=us-central1-a \ --machine-type=e2-medium \ --image-project=freebsd-org-cloud-dev \ --image-family=freebsd-14-1 \ --metadata-from-file startup-script=setup.sh
Where setup.sh contains:
sh#!/bin/sh pkg update -q pkg install -y nginx sysrc nginx_enable="YES" service nginx start
Azure: Deploying FreeBSD on Virtual Machines
Using Azure Marketplace Images
FreeBSD is available in the Azure Marketplace:
shaz vm image list --publisher thefreebsdfoundation --all --output table
Create a VM:
shaz vm create \ --resource-group myResourceGroup \ --name freebsd-web \ --image thefreebsdfoundation:freebsd-14:14_1-release:latest \ --size Standard_B2s \ --admin-username azureuser \ --ssh-key-value ~/.ssh/id_rsa.pub
Azure-Specific Configuration
Install the Azure CLI:
shpkg install py311-azure-cli
The Azure Linux Agent (waagent) is pre-installed on marketplace images and handles provisioning, disk management, and extensions.
Access instance metadata:
shfetch -qo - "http://169.254.169.254/metadata/instance?api-version=2021-02-01" -H "Metadata: true"
Cloud-Init on FreeBSD
Cloud-init is the standard for cloud instance initialization. FreeBSD official images ship with cloud-init pre-configured.
Cloud-Init Configuration
Create a cloud-config YAML file:
shcat > cloud-config.yaml << 'EOF' #cloud-config package_update: true packages: - nginx - git - vim runcmd: - sysrc nginx_enable="YES" - service nginx start - mkdir -p /data/www - chown www:www /data/www write_files: - path: /usr/local/etc/nginx/nginx.conf content: | worker_processes auto; events { worker_connections 1024; } http { server { listen 80; root /data/www; } } users: - name: deploy groups: wheel shell: /bin/sh sudo: ALL=(ALL) NOPASSWD:ALL ssh_authorized_keys: - ssh-rsa AAAA...your-key-here EOF
Pass it to the cloud provider. On AWS:
shaws ec2 run-instances \ --image-id ami-XXXXXXXXX \ --instance-type t3.medium \ --user-data file://cloud-config.yaml \ --key-name your-keypair
Cloud-Init Debugging
If cloud-init does not behave as expected, check the logs:
shcat /var/log/cloud-init.log cat /var/log/cloud-init-output.log
Re-run cloud-init manually:
shcloud-init clean --logs cloud-init init cloud-init modules --mode=config cloud-init modules --mode=final
Building Custom Images with Packer
Official images are a starting point, but production deployments need custom images with your software pre-installed.
Install Packer
shpkg install packer
Packer Template for AWS
Create freebsd-aws.pkr.hcl:
shcat > freebsd-aws.pkr.hcl << 'PEOF' packer { required_plugins { amazon = { version = ">= 1.2.0" source = "github.com/hashicorp/amazon" } } } source "amazon-ebs" "freebsd" { ami_name = "freebsd-14-custom-{{timestamp}}" instance_type = "t3.medium" region = "us-east-1" ssh_username = "ec2-user" source_ami_filter { filters = { name = "FreeBSD 14.*-RELEASE-amd64*" root-device-type = "ebs" virtualization-type = "hvm" } owners = ["782442783595"] most_recent = true } } build { sources = ["source.amazon-ebs.freebsd"] provisioner "shell" { inline = [ "sudo pkg update -q", "sudo pkg install -y nginx git vim-console", "sudo sysrc nginx_enable=YES", "sudo freebsd-update fetch install || true", "sudo pkg clean -ay" ] } } PEOF
Build the image:
shpacker init freebsd-aws.pkr.hcl packer build freebsd-aws.pkr.hcl
Packer Template for GCP
shcat > freebsd-gcp.pkr.hcl << 'PEOF' source "googlecompute" "freebsd" { project_id = "your-project-id" zone = "us-central1-a" machine_type = "e2-medium" ssh_username = "packer" image_name = "freebsd-14-custom-{{timestamp}}" image_family = "freebsd-14-custom" source_image_family = "freebsd-14-1" source_image_project = "freebsd-org-cloud-dev" } build { sources = ["source.googlecompute.freebsd"] provisioner "shell" { inline = [ "sudo pkg update -q", "sudo pkg install -y nginx git", "sudo sysrc nginx_enable=YES", "sudo pkg clean -ay" ] } } PEOF
Terraform: Infrastructure as Code
Install Terraform
shpkg install terraform
AWS FreeBSD Instance with Terraform
Create main.tf:
shcat > main.tf << 'TEOF' terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } provider "aws" { region = "us-east-1" } data "aws_ami" "freebsd" { most_recent = true owners = ["782442783595"] filter { name = "name" values = ["FreeBSD 14.*-RELEASE-amd64*"] } } resource "aws_instance" "web" { ami = data.aws_ami.freebsd.id instance_type = "t3.medium" key_name = "your-keypair" vpc_security_group_ids = [aws_security_group.web.id] user_data = file("cloud-config.yaml") root_block_device { volume_size = 30 volume_type = "gp3" } tags = { Name = "freebsd-web" } } resource "aws_security_group" "web" { name = "freebsd-web-sg" ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["YOUR_IP/32"] } ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } output "public_ip" { value = aws_instance.web.public_ip } TEOF
Deploy:
shterraform init terraform plan terraform apply
GCP FreeBSD Instance with Terraform
shcat > main-gcp.tf << 'TEOF' provider "google" { project = "your-project-id" region = "us-central1" } resource "google_compute_instance" "web" { name = "freebsd-web" machine_type = "e2-medium" zone = "us-central1-a" boot_disk { initialize_params { image = "freebsd-org-cloud-dev/freebsd-14-1" size = 30 type = "pd-ssd" } } network_interface { network = "default" access_config {} } metadata_startup_script = file("setup.sh") } TEOF
Production Checklist
Before putting a FreeBSD cloud instance into production, verify these items:
Security Hardening
sh# Disable password authentication cat >> /etc/ssh/sshd_config << 'EOF' PasswordAuthentication no ChallengeResponseAuthentication no PermitRootLogin no EOF service sshd restart # Enable PF firewall sysrc pf_enable="YES" cat > /etc/pf.conf << 'PFEOF' ext_if="xn0" set skip on lo0 block in all pass out all keep state pass in on $ext_if proto tcp to port {22, 80, 443} keep state PFEOF service pf start # Enable security audit pkg install lynis lynis audit system
Monitoring
shpkg install node_exporter sysrc node_exporter_enable="YES" service node_exporter start
Automated Updates
shecho '0 4 * * * root freebsd-update cron && freebsd-update install' >> /etc/crontab echo '0 5 * * 0 root pkg update -q && pkg upgrade -y' >> /etc/crontab
Backups
For AWS, schedule EBS snapshots. For ZFS-based storage:
shpkg install zfs-periodic
Add to /etc/periodic.conf:
shecho 'daily_zfs_snapshot_enable="YES"' >> /etc/periodic.conf echo 'daily_zfs_snapshot_keep="7"' >> /etc/periodic.conf
FAQ
Q: Are official FreeBSD cloud images free?
A: Yes. The FreeBSD Foundation publishes images at no cost on all three major cloud providers. You only pay for the cloud compute and storage resources.
Q: Which cloud provider has the best FreeBSD support?
A: AWS has the longest history of FreeBSD support and the most mature images. GCP is close behind. Azure support has improved significantly but occasionally lags on new releases.
Q: Does cloud-init work the same on FreeBSD as on Linux?
A: Mostly yes. The core functionality (user creation, package installation, file writing, runcmd) works identically. Some Linux-specific modules (like apt/yum) are replaced with FreeBSD equivalents (pkg).
Q: Can I run ZFS on cloud instances?
A: Yes. ZFS works well on cloud block storage. Use it for data volumes (EBS, Persistent Disks) for snapshots, compression, and checksumming. The root disk on official images typically uses UFS.
Q: How do I resize the root disk on a FreeBSD cloud instance?
A: Resize the cloud volume first (e.g., EBS modify-volume), then on the instance: gpart recover da0, gpart resize -i 2 da0, growfs /dev/da0p2.
Q: Is FreeBSD good for Kubernetes nodes?
A: FreeBSD does not run Kubernetes natively (no Linux containers). Use FreeBSD for workloads that benefit from jails, ZFS, and the FreeBSD network stack. Run Kubernetes on Linux if you need it.
Q: Can I use Ansible to manage FreeBSD cloud instances?
A: Yes. Install Python on the FreeBSD instance (pkg install python311) and use the community.general Ansible collection which includes FreeBSD-specific modules for pkg, sysrc, and service management.
Q: How do I handle instance storage vs persistent storage?
A: Treat instance storage as ephemeral. Put application state on persistent volumes (EBS, Persistent Disks). Use user-data/cloud-init to rebuild the instance configuration on every boot.