Skip to content

Instantly share code, notes, and snippets.

@cs224
Created March 7, 2025 06:10
Show Gist options
  • Save cs224/e120e094fc2114d84eb43618262c328c to your computer and use it in GitHub Desktop.
Save cs224/e120e094fc2114d84eb43618262c328c to your computer and use it in GitHub Desktop.
Incus/LXD as an Alternative to Vagrant for DevOps Testing
config:
boot.autostart: "false"
# Enable nested containers (needed for Docker-in-LXD).
security.nesting: "true"
user.user-data: |
#cloud-config
output:
all: '| tee -a /var/log/cloud-init-output.log'
apt_update: true
apt_upgrade: true
packages:
- openssh-server
- ca-certificates
- curl
- wget
- emacs-nox
- wajig
- unzip
- p7zip-full
- make
- apt-file
- tree
users:
- name: ubuntu
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- @@SSH_KEY@@
description: "Profile for devops container with cloud-init (script-generated)"
devices: {}
name: devops-profile
#!/usr/bin/env bash
#
# incus-profile-install.sh
#
# Create/update an Incus profile named "devops-profile" using a Cloud-Init template.
#
# Usage:
# ./incus-profile-install.sh [optional-profile-name]
#
# By default, this script:
# - reads SSH key from ~/.ssh/id_rsa.pub
# - substitutes it into incus-cloud-init.yaml.in
# - applies the result to an Incus profile named "devops-profile" (or $1 if provided)
set -eu
# 1) Decide the profile name (default: devops-profile)
PROFILE_NAME="${1:-devops-profile}"
# 2) Where is your SSH key?
SSH_KEY_PATH="${HOME}/.ssh/id_rsa.pub"
# 3) Template input and final output
TEMPLATE_FILE="incus-cloud-init.yaml.in"
OUTPUT_FILE="incus-cloud-init.yaml"
# --- Check that the SSH key file exists ---
if [[ ! -f "${SSH_KEY_PATH}" ]]; then
echo "ERROR: SSH key not found at ${SSH_KEY_PATH}"
exit 1
fi
# --- Check that our template file exists ---
if [[ ! -f "${TEMPLATE_FILE}" ]]; then
echo "ERROR: Template file ${TEMPLATE_FILE} not found."
exit 1
fi
# --- Read the public key from file ---
SSH_KEY_CONTENT="$(cat "${SSH_KEY_PATH}")"
# --- Substitute the placeholder in the template ---
# Using '|' as a sed delimiter to avoid issues if the key has slashes
sed "s|@@SSH_KEY@@|${SSH_KEY_CONTENT}|g" "${TEMPLATE_FILE}" > "${OUTPUT_FILE}"
echo "Generated ${OUTPUT_FILE} with your SSH key."
# --- Create or update the Incus profile ---
# We can check if the profile exists by trying to show it
if incus profile show "${PROFILE_NAME}" >/dev/null 2>&1; then
echo "Profile '${PROFILE_NAME}' exists. Updating it..."
incus profile edit "${PROFILE_NAME}" < "${OUTPUT_FILE}"
else
echo "Profile '${PROFILE_NAME}' does not exist. Creating it..."
incus profile create "${PROFILE_NAME}" < "${OUTPUT_FILE}"
fi
echo "Done. Profile '${PROFILE_NAME}' is updated with new Cloud-Init data."
config:
boot.autostart: "false"
# Enable nested containers (needed for Docker-in-LXD).
security.nesting: "true"
user.user-data: |
#cloud-config
output:
all: '| tee -a /var/log/cloud-init-output.log'
apt_update: true
apt_upgrade: true
packages:
- openssh-server
- ca-certificates
- curl
- wget
- emacs-nox
- wajig
- unzip
- p7zip-full
- make
- apt-file
- tree
# Create an 'ubuntu' user with passwordless sudo & your SSH key
users:
- name: ubuntu
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- @@SSH_KEY@@
# Additional commands to run after packages are installed
runcmd:
# 1) Prepare Docker's official apt repo, install Docker CE
- |
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo \"${UBUNTU_CODENAME:-$VERSION_CODENAME}\") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
# Install Docker + plugins
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Optionally allow 'ubuntu' user to use Docker without sudo:
sudo usermod -aG docker ubuntu
# 2) Install Kopia
- |
sudo install -m 0755 -d /etc/apt/keyrings
curl -s https://kopia.io/signing-key | gpg --dearmor | sudo tee /etc/apt/keyrings/kopia-keyring.gpg >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/kopia-keyring.gpg] http://packages.kopia.io/apt/ stable main" | sudo tee /etc/apt/sources.list.d/kopia.list
sudo apt-get update
sudo apt-get install -y kopia
# 3) Install Rclone (script from rclone.org)
- |
curl https://rclone.org/install.sh | sudo bash
description: "Recovery test profile with Docker, Kopia, Rclone, extra packages"
devices: {}
name: recovery-test-profile
#!/usr/bin/env bash
#
# incus-recovery-profile-install.sh
#
# Creates or updates an Incus profile called "recovery-test-profile"
# using a Cloud-Init YAML template that includes Docker/Kopia/Rclone setup.
#
# Usage:
# ./incus-recovery-profile-install.sh [optional-profile-name]
#
# This script:
# - reads SSH key from ~/.ssh/id_rsa.pub
# - substitutes @@SSH_KEY@@ in incus-recovery-cloud-init.yaml.in
# - applies the result to an Incus profile named "recovery-test-profile"
# (or $1 if provided)
set -eu
# 1) Decide the profile name (default: recovery-test-profile)
PROFILE_NAME="${1:-recovery-test-profile}"
# 2) Where is your SSH key?
SSH_KEY_PATH="${HOME}/.ssh/id_rsa.pub"
# 3) Template input and final output
TEMPLATE_FILE="incus-recovery-cloud-init.yaml.in"
OUTPUT_FILE="incus-recovery-cloud-init.yaml"
# --- Check that the SSH key file exists ---
if [[ ! -f "${SSH_KEY_PATH}" ]]; then
echo "ERROR: SSH key not found at ${SSH_KEY_PATH}"
exit 1
fi
# --- Check that our template file exists ---
if [[ ! -f "${TEMPLATE_FILE}" ]]; then
echo "ERROR: Template file ${TEMPLATE_FILE} not found."
exit 1
fi
# --- Read the public key from file ---
SSH_KEY_CONTENT="$(cat "${SSH_KEY_PATH}")"
# --- Substitute the placeholder in the template ---
# Using '|' as a sed delimiter to avoid issues if the key has slashes
sed "s|@@SSH_KEY@@|${SSH_KEY_CONTENT}|g" "${TEMPLATE_FILE}" > "${OUTPUT_FILE}"
echo "Generated ${OUTPUT_FILE} with your SSH key."
# --- Create or update the Incus profile ---
if incus profile show "${PROFILE_NAME}" >/dev/null 2>&1; then
echo "Profile '${PROFILE_NAME}' exists. Updating it..."
incus profile edit "${PROFILE_NAME}" < "${OUTPUT_FILE}"
else
echo "Profile '${PROFILE_NAME}' does not exist. Creating it..."
incus profile create "${PROFILE_NAME}" < "${OUTPUT_FILE}"
fi
echo "Done. Profile '${PROFILE_NAME}' is updated with new Cloud-Init data."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment