Tools: Stop Cloning Stale Hostnames: Practical `systemd-firstboot` for Linux Images (2026)

Tools: Stop Cloning Stale Hostnames: Practical `systemd-firstboot` for Linux Images (2026)

Why use systemd-firstboot instead of editing files yourself?

What it can configure

Example 1: Initialize an offline root directory

Example 2: Work directly on a disk image

Important machine ID rule

Pattern A: generate one during image preparation

Pattern B: reset first-boot-managed files so the target config happens on first boot

Example 3: Seed a root password without exposing plaintext in ps

Example 4: Copy host settings, but be selective

Example 5: Force an update when files already exist

A practical golden-image workflow

During image build

Before sealing the template

After clone or deployment

What systemd-firstboot is not for

Troubleshooting notes

--setup-machine-id does nothing on a live system

--root-shell fails for an offline root

--reset seems aggressive

Final take

References If you build Linux images for VMs, lab machines, edge devices, or golden templates, you have probably hit the same mess at least once. You clone an image, boot it, and realize it still carries a stale hostname, the wrong timezone, or a machine identity you never meant to duplicate. systemd-firstboot is a small tool that solves exactly that class of problem. It writes first-boot configuration directly into an offline root filesystem or disk image, before the system ever starts. That makes it useful when you want image builds to stay reproducible, but you still need a clean way to initialize the parts that should be unique or environment-specific. In this guide, I will show a practical workflow for: You can write /etc/hostname, /etc/locale.conf, /etc/machine-id, and /etc/localtime by hand. But systemd-firstboot gives you a few advantages: It also operates directly on the filesystem, without needing the target system to be booted. That is the key difference from tools like hostnamectl, timedatectl, or localectl. According to the upstream manual, systemd-firstboot can initialize: That is a solid set of knobs for image preparation. Let’s start with the simplest case: you have a mounted root filesystem at /mnt/golden-root. A quick verification pass: Expected shape of the results: If your build pipeline produces a raw disk image instead of a mounted root directory, --image= is usually more convenient. This is especially handy in image-building workflows where you do not want to mount partitions manually first. The machine ID should be unique per instance. If you ship multiple clones with the same populated /etc/machine-id, some software will treat them as the same machine identity. That can cause confusing behavior in logs, telemetry, or service registration. For offline images, use one of these patterns: Use this when the image itself is the final deployed system. The --reset option removes files managed by systemd-firstboot, so the next boot is treated as first boot again. I like this pattern for reusable templates that should be finalized only after cloning. The manual explicitly warns against placing plaintext passwords on the command line, because other users may be able to see them via ps. A safer workflow is to pass a hashed password. Generate a SHA-512 hash: You will be prompted for the password instead of placing it in shell history. Then apply it to the offline image: If you need fully non-interactive automation, store the hash in your secret manager or CI secret store and inject it at runtime. Afterward, verify that passwd and shadow were created inside the target root: systemd-firstboot can copy some settings from the build host. This is convenient, but I would use it carefully. For reproducible image builds, explicit values are usually better than inheriting whatever happens to be configured on the build machine that day. By default, systemd-firstboot does not overwrite existing configuration files. That is a good default, but it can surprise you if you are iterating on an image and nothing seems to change. Use --force when you really do want replacement behavior: Without --force, existing files are left alone. Here is a pattern I trust for VM templates and appliance-style images. Reset first-boot-managed files if clones should personalize later: Either let systemd-firstboot.service prompt on first boot where appropriate, or inject settings during provisioning. That split keeps the image generic while still using supported systemd-native tooling. A few boundaries matter here. Do not use it as a general configuration-management replacement. It is not Ansible, not cloud-init, and not a full provisioning engine. It is best for basic early identity and boot-adjacent settings. Also, it is not recommended as your normal interface for changing a running system that is already configured. For live systems, use the regular tools: That is expected. The manual notes that machine ID setup with --setup-machine-id is for use with --root= or --image=. The shell path must exist inside the target root. If your image does not contain /bin/bash, setting --root-shell=/bin/bash will fail. It is. --reset removes files configured by systemd-firstboot so the next boot is treated as first boot again. Use it intentionally, ideally near the end of an image pipeline. systemd-firstboot is one of those tools that feels small until you start building reusable Linux images regularly. Then it becomes a very clean answer to a real operational problem: how do you prepare an image without baking in the identity that should only exist after deployment? If you are shipping templates, appliances, lab VMs, or self-hosted images, it is worth adding to your toolbox. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --locale=en_US.UTF-8 \ --timezone=UTC \ --hostname=web-template \ --setup-machine-id -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --locale=en_US.UTF-8 \ --timezone=UTC \ --hostname=web-template \ --setup-machine-id -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --locale=en_US.UTF-8 \ --timezone=UTC \ --hostname=web-template \ --setup-machine-id -weight: 600;">sudo cat /mnt/golden-root/etc/locale.conf -weight: 600;">sudo cat /mnt/golden-root/etc/hostname -weight: 600;">sudo cat /mnt/golden-root/etc/machine-id -weight: 600;">sudo readlink /mnt/golden-root/etc/localtime -weight: 600;">sudo cat /mnt/golden-root/etc/locale.conf -weight: 600;">sudo cat /mnt/golden-root/etc/hostname -weight: 600;">sudo cat /mnt/golden-root/etc/machine-id -weight: 600;">sudo readlink /mnt/golden-root/etc/localtime -weight: 600;">sudo cat /mnt/golden-root/etc/locale.conf -weight: 600;">sudo cat /mnt/golden-root/etc/hostname -weight: 600;">sudo cat /mnt/golden-root/etc/machine-id -weight: 600;">sudo readlink /mnt/golden-root/etc/localtime LANG=en_US.UTF-8 web-template 3d6f5d6d8b714d55a78f55c9e08b0d47 ../usr/share/zoneinfo/UTC LANG=en_US.UTF-8 web-template 3d6f5d6d8b714d55a78f55c9e08b0d47 ../usr/share/zoneinfo/UTC LANG=en_US.UTF-8 web-template 3d6f5d6d8b714d55a78f55c9e08b0d47 ../usr/share/zoneinfo/UTC -weight: 600;">sudo systemd-firstboot \ --image=./debian-golden.raw \ --locale=en_US.UTF-8 \ --timezone=UTC \ --hostname=app-template \ --setup-machine-id -weight: 600;">sudo systemd-firstboot \ --image=./debian-golden.raw \ --locale=en_US.UTF-8 \ --timezone=UTC \ --hostname=app-template \ --setup-machine-id -weight: 600;">sudo systemd-firstboot \ --image=./debian-golden.raw \ --locale=en_US.UTF-8 \ --timezone=UTC \ --hostname=app-template \ --setup-machine-id -weight: 600;">sudo systemd-firstboot --root=/mnt/golden-root --setup-machine-id -weight: 600;">sudo systemd-firstboot --root=/mnt/golden-root --setup-machine-id -weight: 600;">sudo systemd-firstboot --root=/mnt/golden-root --setup-machine-id -weight: 600;">sudo systemd-firstboot --root=/mnt/golden-root --reset -weight: 600;">sudo systemd-firstboot --root=/mnt/golden-root --reset -weight: 600;">sudo systemd-firstboot --root=/mnt/golden-root --reset openssl passwd -6 openssl passwd -6 openssl passwd -6 -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --root-password-hashed='$6$rounds=10000$REPLACE_WITH_REAL_HASH' -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --root-password-hashed='$6$rounds=10000$REPLACE_WITH_REAL_HASH' -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --root-password-hashed='$6$rounds=10000$REPLACE_WITH_REAL_HASH' -weight: 600;">sudo ls -l /mnt/golden-root/etc/passwd /mnt/golden-root/etc/shadow -weight: 600;">sudo ls -l /mnt/golden-root/etc/passwd /mnt/golden-root/etc/shadow -weight: 600;">sudo ls -l /mnt/golden-root/etc/passwd /mnt/golden-root/etc/shadow -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --copy-locale \ --copy-timezone -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --copy-locale \ --copy-timezone -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --copy-locale \ --copy-timezone -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --hostname=web-prod-template \ --timezone=Europe/Berlin \ --force -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --hostname=web-prod-template \ --timezone=Europe/Berlin \ --force -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --hostname=web-prod-template \ --timezone=Europe/Berlin \ --force -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --locale=en_US.UTF-8 \ --timezone=UTC \ --hostname=template-base -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --locale=en_US.UTF-8 \ --timezone=UTC \ --hostname=template-base -weight: 600;">sudo systemd-firstboot \ --root=/mnt/golden-root \ --locale=en_US.UTF-8 \ --timezone=UTC \ --hostname=template-base -weight: 600;">sudo systemd-firstboot --root=/mnt/golden-root --reset -weight: 600;">sudo systemd-firstboot --root=/mnt/golden-root --reset -weight: 600;">sudo systemd-firstboot --root=/mnt/golden-root --reset -weight: 600;">sudo test -x /mnt/golden-root/bin/bash && echo ok -weight: 600;">sudo test -x /mnt/golden-root/bin/bash && echo ok -weight: 600;">sudo test -x /mnt/golden-root/bin/bash && echo ok - setting locale, timezone, and hostname in an offline image - generating a fresh machine ID correctly - pre-seeding root access without putting a plaintext password on the command line - resetting first-boot state when you want an image to ask again - verifying what changed before you ship the image - it understands both offline root directories and disk images - it knows which files correspond to each setting - it avoids overwriting existing values unless you explicitly ask it to - it can generate a fresh machine ID for an offline image - it has a supported reset workflow for returning an image to first-boot state - locale and message locale - keyboard map - kernel command line used by kernel--weight: 500;">install - root password and root shell - writes /mnt/golden-root/etc/locale.conf - creates the /mnt/golden-root/etc/localtime symlink - writes /mnt/golden-root/etc/hostname - creates /mnt/golden-root/etc/machine-id with a random ID - local lab image built on a trusted workstation, where host timezone and locale are intentional - CI runners or shared build hosts, where inherited settings may vary silently - Install packages and application bits. - Set stable defaults that should be common everywhere. - Leave machine-specific values for first-boot time. - hostnamectl - timedatectl - systemd upstream manual, systemd-firstboot(1): https://www.freedesktop.org/software/systemd/man/latest/systemd-firstboot.html - Debian manpage mirror for systemd-firstboot(1): https://manpages.debian.org/bookworm/systemd/systemd-firstboot.1.en.html - ArchWiki overview: https://wiki.archlinux.org/title/Systemd-firstboot