What is Autoinstall (Ubuntu 24.04)?
Autoinstall is Ubuntu Server’s unattended installation in Subiquity. You provide a YAML answer file (user-data) via cloud-init datasources (e.g., NoCloud / NoCloud-Net) referenced by a kernel argument like autoinstall ds="nocloud-net;...". With NoCloud-Net, the installer downloads meta-data and user-data from HTTPS and completes hands-free—ideal for repeatable lab or fleet provisioning.
You will serve per-host NoCloud seeds at:
- https://nocloud.maksonlee.com/b/<ID>/user-data
- https://nocloud.maksonlee.com/b/<ID>/meta-data
This demo uses <ID> = test. Seeds are generated dynamically with NGINX SSI.
Prerequisites
- DNS A/AAAA: nocloud.maksonlee.com→ your server
- Cloudflare DNS API token with Zone → DNS: Edit
- ISO: ubuntu-24.04.2-live-server-amd64.iso
- TCP 80/443 open
- Install NGINX and create directories
sudo apt update
sudo apt install -y nginx
sudo mkdir -p /var/www/html/nocloud/b
sudo chown -R www-data:www-data /var/www/html/nocloud
- Add SSI templates
/var/www/html/nocloud/b/_user.ssi
#cloud-config
autoinstall:
  version: 1
  identity:
    hostname: <!--# echo var="host_id" -->
    username: ubuntu
    password: "$6$PWguV961aHQjVEUh$EbowTJLSSunVj2zejKxIlS9A6iRBoG8J8XKnTukaTvtyW5QUzK.XQDFv9nEDyBpSpEOmQGMteP5ucnBBI6YsV0"
  timezone: Asia/Taipei
  locale: en_US.UTF-8
  keyboard: { layout: us }
  ssh:
    install-server: true
    allow-pw: true
  apt:
    primary: [{ arches: [default], uri: http://archive.ubuntu.com/ubuntu/ }]
  storage:
    layout:
      name: direct
      match: { size: largest }
  user-data:
    preserve_hostname: false
    package_update: true
    packages: [htop]
    runcmd:
      - [bash, -lc, "lsb_release -a || true"]
      - [bash, -lc, "lsblk -f"]
/var/www/html/nocloud/b/_meta.ssi
instance-id: <!--# echo var="host_id" -->
- Initial HTTP configuration (before certificate)
/etc/nginx/sites-available/nocloud
server {
    listen 80;
    server_name nocloud.maksonlee.com;
    root /var/www/html/nocloud;
    index index.html;
    autoindex off;
    types { } default_type application/octet-stream;
    location / {
        limit_except GET HEAD { deny all; }
        try_files $uri =404;
        add_header X-Content-Type-Options "nosniff";
        add_header Cache-Control "public, max-age=60";
    }
    location ~ ^/b/([A-Za-z0-9._-]+)/user-data$ {
        default_type text/plain;
        ssi on; ssi_types *;
        set $host_id $1;
        try_files /b/_user.ssi =404;
        add_header Cache-Control "no-store";
    }
    location ~ ^/b/([A-Za-z0-9._-]+)/meta-data$ {
        default_type text/plain;
        ssi on; ssi_types *;
        set $host_id $1;
        try_files /b/_meta.ssi =404;
        add_header Cache-Control "no-store";
    }
}
Enable and reload:
sudo ln -sf /etc/nginx/sites-available/nocloud /etc/nginx/sites-enabled/nocloud
sudo nginx -t && sudo systemctl reload nginx
curl -s http://nocloud.maksonlee.com/b/test/meta-data
curl -s http://nocloud.maksonlee.com/b/test/user-data
- Install Certbot and issue certificate with Cloudflare DNS-01
sudo apt update
sudo apt install -y certbot python3-certbot-dns-cloudflare
sudo mkdir -p /etc/letsencrypt
sudo bash -c 'cat > /etc/letsencrypt/cloudflare.ini <<EOF
dns_cloudflare_api_token = YOUR_CLOUDFLARE_DNS_TOKEN
EOF'
sudo chmod 600 /etc/letsencrypt/cloudflare.ini
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d nocloud.maksonlee.com \
  -m admin@maksonlee.com --agree-tos --non-interactive
Verify:
sudo ls -l /etc/letsencrypt/live/nocloud.maksonlee.com/
- Final HTTPS configuration
/etc/nginx/sites-available/nocloud
server {
    server_name nocloud.maksonlee.com;
    root /var/www/html/nocloud;
    index index.html;
    autoindex off;
    types { } default_type application/octet-stream;
    location / {
        limit_except GET HEAD { deny all; }
        try_files $uri =404;
        add_header X-Content-Type-Options "nosniff";
        add_header Cache-Control "public, max-age=60";
    }
    location ~ ^/b/([A-Za-z0-9._-]+)/user-data$ {
        default_type text/plain;
        ssi on; ssi_types *;
        set $host_id $1;
        try_files /b/_user.ssi =404;
        add_header Cache-Control "no-store";
    }
    location ~ ^/b/([A-Za-z0-9._-]+)/meta-data$ {
        default_type text/plain;
        ssi on; ssi_types *;
        set $host_id $1;
        try_files /b/_meta.ssi =404;
        add_header Cache-Control "no-store";
    }
    listen 443 ssl;
    ssl_certificate     /etc/letsencrypt/live/nocloud.maksonlee.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nocloud.maksonlee.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;
}
server {
    listen 80;
    server_name nocloud.maksonlee.com;
    return 301 https://$host$request_uri;
}
Reload and test:
sudo nginx -t && sudo systemctl reload nginx
curl -s https://nocloud.maksonlee.com/b/test/meta-data
curl -s https://nocloud.maksonlee.com/b/test/user-data | head -n 20
- Install — add the GRUB kernel parameters
At the GRUB edit screen, append to the linux line after ---:
autoinstall ds="nocloud-net;s=https://nocloud.maksonlee.com/b/test/"


- Verify after install
hostnamectl
cat /etc/hostname
Expected: test.

Scale out
- For each host, change only <ID>in the URL:
autoinstall ds="nocloud-net;s=https://nocloud.maksonlee.com/b/web-01/"
- instance-idequals- <ID>for clean first-boot runs.
- Add packages in _user.ssi, or create variants with additional SSI templates and routes.
Appendix: create a SHA-512 password hash
mkpasswd -m sha-512 'changeme'
