Files
golden-image/n8n
2026-02-23 21:10:15 +00:00

214 lines
5.3 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# ============================================================
# Golden Image Builder for Ubuntu 24.x (Example: Docker + n8n)
# - Installs prerequisites, Docker, n8n
# - Cleans system for templating (cloud-init, machine-id, SSH keys, logs)
# - Powers off at the end
# ============================================================
APP="${APP:-n8n}"
TIMEZONE="${TIMEZONE:-America/New_York}"
# n8n defaults inside the image (customer-specific values can be overridden later)
N8N_HOST_DEFAULT="${N8N_HOST_DEFAULT:-localhost}"
N8N_PROTOCOL_DEFAULT="${N8N_PROTOCOL_DEFAULT:-http}"
WEBHOOK_URL_DEFAULT="${WEBHOOK_URL_DEFAULT:-http://localhost:5678/}"
LOG="/root/golden-image-${APP}.log"
require_root() {
if [[ "${EUID}" -ne 0 ]]; then
echo "ERROR: Run as root. Example: sudo bash $0"
exit 1
fi
}
log() {
echo "[$(date -Is)] $*" | tee -a "$LOG"
}
check_ubuntu() {
if [[ ! -f /etc/os-release ]]; then
log "ERROR: /etc/os-release not found."
exit 1
fi
. /etc/os-release
if [[ "${ID}" != "ubuntu" ]]; then
log "ERROR: This script is for Ubuntu. Detected: ${ID}"
exit 1
fi
if [[ "${VERSION_ID}" != 24.* ]]; then
log "WARN: Expected Ubuntu 24.x. Detected: ${VERSION_ID}. Proceeding anyway."
fi
log "OS: ${PRETTY_NAME}"
}
apt_base() {
log "Updating system and installing base packages..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get upgrade -y
apt-get install -y --no-install-recommends \
ca-certificates curl gnupg lsb-release \
cloud-init qemu-guest-agent \
sudo vim-tiny unzip
systemctl enable --now qemu-guest-agent || true
}
install_docker() {
log "Installing Docker Engine + Compose plugin (official repo)..."
install -m 0755 -d /etc/apt/keyrings
if [[ ! -f /etc/apt/keyrings/docker.gpg ]]; then
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
fi
. /etc/os-release
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu ${VERSION_CODENAME} stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update -y
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
systemctl enable --now docker
docker --version | tee -a "$LOG"
docker compose version | tee -a "$LOG"
}
setup_timezone() {
log "Setting timezone: ${TIMEZONE}"
timedatectl set-timezone "${TIMEZONE}" || true
}
install_n8n() {
log "Installing ${APP}..."
mkdir -p /opt/n8n
cat >/opt/n8n/docker-compose.yml <<'YAML'
services:
n8n:
image: n8nio/n8n:latest
restart: always
ports:
- "5678:5678"
environment:
- N8N_HOST=${N8N_HOST}
- N8N_PROTOCOL=${N8N_PROTOCOL}
- N8N_PORT=5678
- WEBHOOK_URL=${WEBHOOK_URL}
- TZ=${TZ}
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
YAML
cat >/opt/n8n/.env <<ENV
N8N_HOST=${N8N_HOST_DEFAULT}
N8N_PROTOCOL=${N8N_PROTOCOL_DEFAULT}
WEBHOOK_URL=${WEBHOOK_URL_DEFAULT}
TZ=${TIMEZONE}
ENV
(cd /opt/n8n && docker compose up -d)
docker ps | tee -a "$LOG"
}
# Optional: ensure SSH host keys are regenerated on first boot (after we delete them)
configure_ssh_regen() {
log "Ensuring SSH host keys will be generated on first boot..."
# Ubuntu normally generates keys if missing, but we make it explicit.
cat >/etc/systemd/system/ssh-hostkey-regen.service <<'UNIT'
[Unit]
Description=Regenerate SSH host keys if missing
After=network.target
Before=ssh.service
ConditionPathExists=!/etc/ssh/ssh_host_rsa_key
[Service]
Type=oneshot
ExecStart=/usr/bin/ssh-keygen -A
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable ssh-hostkey-regen.service || true
}
cleanup_for_template() {
log "CLEANUP: preparing VM for templating..."
log "Stopping services..."
systemctl stop docker || true
systemctl stop ssh || true
log "Cleaning cloud-init state + logs..."
cloud-init clean --logs || true
rm -f /etc/cloud/cloud.cfg.d/99-installer.cfg 2>/dev/null || true
log "Resetting machine-id..."
truncate -s 0 /etc/machine-id || true
rm -f /var/lib/dbus/machine-id || true
ln -sf /etc/machine-id /var/lib/dbus/machine-id || true
log "Removing SSH host keys..."
rm -f /etc/ssh/ssh_host_* || true
log "Clearing bash history..."
rm -f /root/.bash_history || true
for d in /home/*; do
[[ -d "$d" ]] && rm -f "$d/.bash_history" || true
done
log "Cleaning apt cache..."
apt-get autoremove -y || true
apt-get clean || true
rm -rf /var/lib/apt/lists/* || true
log "Vacuuming journald..."
journalctl --rotate || true
journalctl --vacuum-time=1s || true
log "Removing logs (keeping directories)..."
find /var/log -type f -exec truncate -s 0 {} \; || true
log "Removing temporary files..."
rm -rf /tmp/* /var/tmp/* || true
log "Sync..."
sync
}
poweroff_vm() {
log "DONE. Powering off now (convert VM to template in Proxmox)."
poweroff
}
main() {
require_root
: > "$LOG"
check_ubuntu
setup_timezone
apt_base
case "$APP" in
n8n)
install_docker
install_n8n
configure_ssh_regen
;;
*)
log "ERROR: Unsupported APP='${APP}'. Supported: n8n"
exit 1
;;
esac
cleanup_for_template
poweroff_vm
}
main "$@"