A fully unattended Fedora Linux installation system that takes a machine from a blank drive to a complete, personalised developer workstation — without a single manual step after booting the ISO.
Setting up a new Linux workstation typically means hours of manual work: partitioning drives, installing packages one by one, configuring repositories, hunting down drivers, and restoring personal files. This project eliminates all of that.
A single script downloads the latest Fedora installer, merges a complete installation recipe into it, and produces a bootable ISO. Boot from that ISO on any compatible machine and walk away — the system installs itself, configures everything, and is ready to use when it reboots.
This is useful for:
- Rebuilding a workstation quickly after hardware failure
- Provisioning multiple machines with an identical baseline
- Ensuring a reproducible, documented system configuration
unattend.ks — The Installation Recipe
A Kickstart file is a plain-text script that the Fedora installer (Anaconda) reads to answer every prompt that would otherwise require human input. It describes the entire system: disk layout, user accounts, locale settings, packages to install, and arbitrary shell commands to run after installation.
This file is intentionally safe to store in a public repository — all secrets (user password, fileshare credentials) are represented as placeholders that are substituted at build time, never committed.
build-iso.sh — The Build Script
A Bash script that orchestrates the build process. It:
- Prompts interactively for secrets (credentials and user password)
- Downloads the latest Fedora netinstall ISO automatically
- Performs a safe, in-memory substitution of secrets into a temporary copy of the kickstart file
- Uses
mkksisoto embed the kickstart into the ISO so no boot parameters need to be set manually
build-iso.sh
│
├── Prompt for fileshare credentials (x2 confirm)
├── Prompt for user password (x2 confirm, converted to SHA-512 hash)
├── Detect latest Fedora version
├── Download Fedora-Everything-netinstall-x86_64-<ver>.iso
├── Inject secrets into a temporary kickstart copy
└── mkksiso → fedora-kde.iso
Boot fedora-kde.iso
│
├── %pre — Auto-detect target disk, write partition layout
├── Install — Pull packages from Fedora mirrors
└── %post — Configure everything (see below)
| Requirement | Notes |
|---|---|
| Fedora Linux (or compatible) | mkksiso is from the lorax package |
lorax |
sudo dnf install lorax |
openssl |
For hashing the user password; installed by default on most systems |
curl |
For downloading the netinstall ISO |
| Internet access | To fetch the ISO and detect the latest Fedora version |
| Requirement | Notes |
|---|---|
| x86_64 machine or VM | Physical or virtual, UEFI firmware |
| 20 GB+ disk space | More recommended for development use |
| Internet access | Installation pulls packages from Fedora mirrors |
# Clone the repository
git clone https://github.com/SamScripting/bootstrap.git
cd bootstrap
# Build the ISO (prompts for all secrets interactively)
./build-iso.sh
# Optionally specify an output filename
./build-iso.sh my-workstation.isoThe script will ask for:
- Fileshare username — for fetching SSH keys during post-install
- Fileshare password — entered twice to confirm
- Password for user
sam— entered twice to confirm; converted to a SHA-512 hash before being written anywhere
When the build completes, write the ISO to a USB drive or attach it as a VM boot image.
On Linux:
sudo dd if=fedora-kde.iso of=/dev/sdX bs=4M status=progressBoot the target machine from the ISO. The installation is fully automatic — no prompts, no interaction required.
- KDE Plasma — Full desktop environment (
@^kde-desktop-environment) - Chromium — Web browser
| Component | Detail |
|---|---|
| EFI partition | 600 MB, vfat |
/boot |
2 GB, ext4 |
| Root + Home | Remaining space, Btrfs with separate root and home subvolumes, zstd:1 compression |
| Swap | Via zram — compressed RAM-based swap, no dedicated partition |
The target disk is auto-detected at install time using lsblk, so the kickstart works regardless of whether the disk is /dev/sda, /dev/nvme0n1, or any other name.
| Tool | Description |
|---|---|
git |
Version control |
gh |
GitHub CLI |
nodejs + npm |
JavaScript runtime and package manager |
podman + podman-compose + podman-docker |
Rootless container runtime with Docker compatibility |
rclone |
Cloud storage sync |
| Docker CE | Full Docker engine, with the current user added to the docker group |
| Visual Studio Code | Installed from Microsoft's official repository |
| PowerShell | Cross-platform shell and scripting environment |
| Claude Code | Anthropic's AI coding assistant (via npm) |
| Gemini CLI | Google's AI assistant (via npm) |
Installed automatically for the sam user:
anthropic.claude-codegithub.github-vscode-themegoogle.geminicodeassistmermaidchart.vscode-mermaid-chartms-python.python+ms-python.vscode-pylance+ms-python.debugpy+ms-python.vscode-python-envsms-vscode.powershellredhat.vscode-yaml
RPM Fusion (free and non-free) repositories are added, then:
- Full FFmpeg (swapped from the restricted
ffmpeg-free) - Complete GStreamer plugin stack
- H.264 support via Cisco's OpenH264 (Firefox video fix)
- Hardware-accelerated video decode via VA-API
The installer detects the GPU vendor at post-install time using lspci and installs the appropriate stack:
| Detected GPU | What Gets Installed |
|---|---|
| AMD | mesa-va-drivers-freeworld, mesa-vdpau-drivers-freeworld |
| Intel | Same mesa freeworld drivers + intel-media-driver |
| NVIDIA | Prerequisites (kernel-devel, libglvnd-*, etc.), akmod-nvidia, xorg-x11-drv-nvidia-cuda. RTX 4000+ series additionally sets the open kernel module flag. |
| VM / none detected | Skips driver-specific packages gracefully |
Flathub is configured as the package source (replacing Fedora's limited default remote):
- Telegram Desktop
- Zoom
- Gearlever (AppImage manager)
- SELinux — enforcing
- Firewall — enabled, SSH allowed
- Root account — locked;
sudoaccess via thesamuser - AppImage support —
fuseandfuse-libsinstalled - Btrfs tools —
btrfs-progsfor filesystem management
The installer clones a private backup repository from GitHub and restores:
.bashrc- SSH
configandknown_hosts(symlinked) - KDE Plasma configuration files (
plasmashellrc,kwinrc,kdeglobals, etc.) - A Logitech receiver
udevrule
SSH private and public keys (fedora, github, opnbackup) are fetched securely over HTTPS from a password-protected private fileshare and placed in ~/.ssh with correct permissions (600 for private keys, 644 for public keys).
The kickstart file committed to this repository contains only placeholders:
--password=CHANGEME_HASH ← replaced by build-iso.sh at build time
KS_USER="CHANGEME" ← replaced by build-iso.sh at build time
KS_PASS="CHANGEME" ← replaced by build-iso.sh at build time
build-iso.sh performs the substitution into a mktemp temporary file, which is deleted automatically when the script exits (via trap). The real values are never written to disk in the repository directory.
- Passwords are read with
read -rsp(silent, no echo) - Each secret is confirmed by entering it twice
- The user password is converted immediately to a SHA-512 hash with
openssl passwd -6; the plaintext is unset from memory before any further work - Fileshare credentials are unset from memory immediately after the
sedsubstitution - The
escape_sedfunction ensures that special characters in passwords (&,\) cannot corrupt the substitution
- Root login is disabled (
rootpw --lock) - The
samuser hassudoaccess via thewheelgroup - SSH keys are stored with strict permissions and are not readable by other users
- SELinux runs in enforcing mode
Search for sam throughout unattend.ks and replace with your username. Key locations:
user --name=samdirectiveusermod -aG docker sam- All
/home/sam/paths in%post
The %packages section (near the top of unattend.ks) lists packages installed directly from Fedora's repositories. Additional packages installed from third-party sources are handled in the %post section.
The %pre section auto-detects the first available disk and generates the partition directives. To modify the layout (sizes, filesystem types, number of subvolumes), edit the cat > /tmp/disk-setup.ks heredoc in %pre.
By default, build-iso.sh detects and downloads the latest stable Fedora release, and the installation source uses $releasever to match whatever version is on the ISO. To pin to a specific version, set FEDORA_VER manually in build-iso.sh before the download step.
bootstrap/
├── unattend.ks # Kickstart installation recipe (safe for public repos)
├── build-iso.sh # Build script: prompts for secrets, downloads ISO, builds output
└── README.md # This file
url --mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=fedora-$releasever&arch=$basearch
$releasever and $basearch are Anaconda/DNF variables resolved at install time from the ISO's embedded RPM database — not shell variables. This ensures the correct package versions are pulled for whatever Fedora release is on the ISO.
DISK=$(lsblk -dpno NAME,TYPE | awk '$2=="disk" && $1 !~ /zram/ { print $1; exit }')Finds the first block device of type disk, excluding zram devices, and uses it as the installation target.
openssl passwd -6 produces a SHA-512 crypt hash compatible with /etc/shadow and Anaconda's --iscrypted user directive. This replaces the deprecated Python crypt module.
dnf install -y \
https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm \
https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm$(rpm -E %fedora) is the shell equivalent of $releasever, expanding to the Fedora version number at runtime. This ensures the correct RPM Fusion release package is fetched regardless of which Fedora version is being installed.