SpaceOS is a personal image-based Linux distribution built on Fedora Bootc 44. It uses Sway as its graphical
environment, is delivered as an OCI image, and receives transactional system updates through bootc. This
repository contains the system definition, desktop configuration, GHCR publishing automation, and a script for
building an Anaconda installer ISO locally.
For the story behind the project, see How and why I built my own bootc-based Linux distribution. The article covers the path from the initial idea to an encrypted dual-boot installation, DisplayLink support, and the CI/CD update pipeline used by SpaceOS.
Important
SpaceOS is built specifically for my hardware, workflow, and personal preferences. I do not recommend using it directly as a general-purpose distribution. You are, however, encouraged to fork this repository and use it as a starting point for building your own bootc-based distribution.
- Fedora Bootc 44 base image.
- Transactional system images published to GitHub Container Registry.
- Lightweight desktop built around Sway, Waybar, Rofi, and Alacritty.
- Graphical installer based on Anaconda.
- Background updates without forced restarts.
- Previous deployment available for rollback.
- DisplayLink support with an EVDI module built for the image kernel.
- Custom Plymouth branding for the boot and LUKS unlock screens.
- A consistent visual style based on Ayu Dark and Ayu Light across the desktop and supported applications.
Installer images are not published as GitHub Release assets because the complete ISO exceeds GitHub's per-file
size limit. Build the installer locally from the repository instead. The script requires Podman, root access through
sudo, and enough free disk space for the container images and resulting ISO.
-
Clone the repository and check out the release tag to install:
git clone https://github.com/SpaceShaman/spaceos.git cd spaceos git checkout vX.Y.Z -
Build the ISO. By default, the script reads the latest local release tag for the installer version and embeds the
ghcr.io/spaceshaman/spaceos:autoimage so that the installed system continues tracking the automatic update channel:./scripts/build-installer-iso.sh
To select a version explicitly, use
VERSIONwithout the leadingv:VERSION=X.Y.Z ./scripts/build-installer-iso.sh
BASE_IMAGE,PAYLOAD_IMAGE,BUILDER_IMAGE, andOUTPUT_DIRcan also be overridden when testing a different image or build environment. -
Verify the generated checksum:
cd output/installer-vX.Y.Z sha256sum --check SpaceOS-vX.Y.Z-x86_64.iso.sha256 -
Write the ISO to a USB drive using a tool such as Fedora Media Writer.
-
Boot the computer from the prepared installation media.
-
Complete the installation using the graphical Anaconda installer.
The SpaceOS image used for installation is embedded in the ISO. After installation, the system tracks the auto
channel in GHCR, so future updates do not require the installer media.
An existing bootc-compatible system can switch directly to SpaceOS:
sudo bootc switch ghcr.io/spaceshaman/spaceos:auto
sudo rebootUse the stable channel if the system should receive official SpaceOS releases only:
sudo bootc switch ghcr.io/spaceshaman/spaceos:stable
sudo rebootBack up important data before switching. SpaceOS contains configuration tailored to specific hardware and may change assumptions about services, the desktop, drivers, or the target system layout.
Use bootc install to-filesystem to install SpaceOS on filesystems that have already been created and formatted. Replace the device paths below, verify them carefully, and run:
set -euo pipefail
ROOT_DEVICE="/dev/ROOT_DEVICE"
BOOT_DEVICE="/dev/BOOT_DEVICE"
EFI_DEVICE="/dev/EFI_DEVICE"
TARGET="/mnt/spaceos"
IMAGE="ghcr.io/spaceshaman/spaceos:auto"
sudo mkdir -p "$TARGET"
sudo mount "$ROOT_DEVICE" "$TARGET"
sudo mkdir -p "$TARGET/boot"
sudo mount "$BOOT_DEVICE" "$TARGET/boot"
sudo mkdir -p "$TARGET/boot/efi"
sudo mount "$EFI_DEVICE" "$TARGET/boot/efi"
findmnt -R "$TARGET"
ROOT_UUID="$(sudo blkid -s UUID -o value "$ROOT_DEVICE")"
BOOT_UUID="$(sudo blkid -s UUID -o value "$BOOT_DEVICE")"
sudo podman pull "$IMAGE"
sudo podman run --rm --privileged \
--pid=host \
--ipc=host \
--security-opt label=type:unconfined_t \
-v /dev:/dev \
-v /var/lib/containers:/var/lib/containers \
-v "${TARGET}:/target" \
"$IMAGE" \
bootc install to-filesystem \
--bootloader=grub \
--root-mount-spec="UUID=${ROOT_UUID}" \
--boot-mount-spec="UUID=${BOOT_UUID}" \
/targetThe mounts select the root, /boot, and EFI filesystems, while the UUID options tell the installed system how to find root and /boot during startup. This example uses a separate /boot; other storage layouts may require different mount options or additional --karg arguments. Change auto to stable to follow official releases only.
Images are published as:
ghcr.io/spaceshaman/spaceos
| Tag | Meaning | Retention |
|---|---|---|
v0.1.3 |
Immutable image for a specific release | Kept indefinitely |
stable |
Latest official SpaceOS release | Moving tag |
latest |
Alias for the latest official release | Moving tag |
auto |
Latest release or latest rebuild following a Fedora update | Moving tag |
auto-v0.1.3-20260920-abcdef123456 |
A specific automatic rebuild | Latest five |
Publishing a new vX.Y.Z tag moves stable, latest, and auto to the new release. Once a week, GitHub Actions
checks the digest of quay.io/fedora/fedora-bootc:44. If the base changed, the source of the latest SpaceOS release
is rebuilt and auto is moved to the resulting image. GitHub Actions does not build or publish installer ISOs.
All vX.Y.Z images remain in the registry. Only the five newest dated automatic rebuilds are retained.
SpaceOS does not use an update mechanism that automatically reboots the computer after finding a new image. A custom systemd timer checks for updates every hour, with a randomized delay of up to 10 minutes.
The update process works as follows:
- The system checks the channel selected during installation or the latest
bootc switch. - The update is deferred on a metered connection, when the battery is below 30%, or while the system load is high.
- A new image is downloaded and prepared as a staged deployment.
- The running system is neither restarted nor modified.
- Waybar displays an indicator when a new deployment is ready.
- The update is applied during a normal restart initiated by the user.
Updates can also be inspected and managed directly with bootc:
bootc status
sudo bootc upgrade --check
sudo bootc upgradebootc status shows the currently booted image, any staged update, and the rollback deployment.
bootc upgrade --check checks registry metadata without downloading and staging the complete update. A regular
bootc upgrade downloads the new image and stages it for the next restart, but does not restart the computer.
Automatic updater logs are available through:
journalctl -u spaceos-update.serviceInspect the current, staged, and rollback deployments with:
bootc statusThe following tables list the main applications and tools added by SpaceOS. They do not include system-level dependencies inherited from the Fedora base image.
| Software | Description |
|---|---|
| Sway | Tiling Wayland compositor compatible with the i3 configuration model. |
| swayidle | Turns displays off after 5 minutes of inactivity and restores them on input. |
| Waybar | Status bar showing workspaces, network, audio, battery, and update state. |
| Rofi | Application launcher. |
| Alacritty | GPU-accelerated terminal emulator. |
| greetd + tuigreet | Lightweight login manager used to start the Sway session. |
| Ayu Dark / Ayu Light | Matching GTK, terminal, Waybar, and Rofi themes. |
| Plymouth | Graphical boot screen, including the LUKS unlock prompt. |
| Software | Description |
|---|---|
| Firefox | Web browser. |
| Thunderbird | Email and calendar client. |
| Signal Desktop | Signal messenger distributed as an AppImage. |
| Teams for Linux | Unofficial Microsoft Teams client. |
| ChatGPT | ChatGPT desktop application for Linux. |
| Bruno | API client for designing and testing HTTP requests. |
| qBittorrent | BitTorrent client. |
| VLC | Multimedia player. |
| GIMP | Raster graphics editor. |
| imv | Wayland image viewer. |
| FileZilla | FTP, FTPS, and SFTP client. |
| GNOME Disks | Utility for inspecting and managing storage devices and disk images. |
| Foliate | eBook reader supporting EPUB, MOBI, AZW3, and PDF formats. |
| LibreOffice Calc | Spreadsheet application. |
| LibreOffice Writer | Word processor. |
| ZAP | Security testing tool for web applications. |
| Software | Description |
|---|---|
| Fish | Interactive shell with rich suggestions and completions. |
| Neovim | Text editor with the SpaceOS development configuration. |
| tmux | Terminal multiplexer. |
| Midnight Commander | Two-panel terminal file manager. |
| Git | Version control system. |
| lazygit | Terminal user interface for Git. |
| wlctl | Terminal interface for Wi-Fi and VPN connections managed by NetworkManager. |
| bluetui | Terminal interface for managing Bluetooth devices through BlueZ. |
| Go | Go compiler and development toolchain. |
| Zig | Zig compiler and development toolchain. |
| uv | Fast Python project and package manager. |
| rclone | Command-line sync tool for cloud storage and remote filesystems. |
| rsync | Efficient local and remote file synchronization utility. |
| zk | Plain-text note-taking assistant with a Zettelkasten workflow. |
| jq | Command-line JSON processor. |
| SOPS | Command-line editor for encrypted secrets in configuration files. |
| age | Simple, modern and secure encryption tool. |
| fzf | Fuzzy finder for filtering and selecting items in the terminal. |
| fd | Fast command-line tool for finding files and directories. |
| bat | cat clone with syntax highlighting and Git integration. |
| htop | Interactive process viewer and system monitor. |
| Codex | OpenAI coding agent for the terminal. |
| GitHub Copilot CLI | GitHub coding assistant for the terminal. |
| opencommit | Generates commit messages from staged changes. |
| Translate Shell | Command-line translator using various translation engines. |
| Newsboat | Terminal-based RSS/Atom feed reader. |
| Software | Description |
|---|---|
| Podman | Daemonless OCI container runtime and image builder. |
| Docker | Container environment compatible with the Docker ecosystem. |
| Docker Compose | Definition and orchestration of multi-container environments. |
| Docker Buildx | Extended Docker image builder with multi-platform build support. |
| BlueZ | Bluetooth stack and system service. |
| Wiremix | Terminal mixer for PipeWire. |
| brightnessctl | Display brightness control. |
| grim + slurp + swappy | Wayland screenshot capture, region selection, and annotation. |
| DisplayLink / EVDI | Support for DisplayLink adapters and docking stations. |
Netcat (nc) |
Command-line utility for TCP and UDP connections. |
I love aliases. Life is too short to type the same long command over and over, and my fingers have better things to do. These shortcuts are defined in config.fish for interactive Fish sessions:
| Alias | Runs | What I use it for |
|---|---|---|
o |
open |
Open files and URLs with the default application. |
w, b |
wlctl, bluetui |
Open the Wi-Fi/VPN and Bluetooth terminal interfaces. |
c |
oco |
Generate a commit message with opencommit. |
wiremix, mix, m |
wiremix --config /etc/wiremix/wiremix.toml |
Open the PipeWire mixer with my configuration. |
lazygit, l |
lazygit --use-config-dir /etc/lazygit |
Open lazygit with my configuration. |
v, vim |
nvim |
Edit files in Neovim. |
p |
python3 |
Run Python. |
d, du, dd, dr |
docker compose, docker compose up -d, docker compose down, docker compose restart |
Manage Compose projects. |
dev, devd, devr |
docker compose -f docker-compose.dev.yml with up -d, down, or restart |
Manage the development Compose file. |
t, tp |
trans -b :en, trans -b :pl |
Translate text to English or Polish. |
box |
rclone mount box: ~/box --vfs-cache-mode full --daemon |
Mount my Box remote at ~/box. |
syncbox |
rsync -av --size-only --delete --progress -e ssh box:Muzyka/ Muzyka/ |
Sync music from box into the local Muzyka/ directory. |
newsboat, n |
newsboat --config-file /etc/newsboat/config --url-file /etc/newsboat/urls |
Open Newsboat with the system feed list and configuration. |
g |
codex exec --ephemeral --skip-git-repo-check --sandbox read-only |
Run a prompt with Codex in a read-only sandbox; pass the prompt as arguments or through standard input. Defined as a Fish function. |
Mod refers to the Super/Windows key.
| Shortcut | Action |
|---|---|
Mod + Enter |
Open Alacritty. |
Mod + R |
Open the Rofi application launcher. |
Mod + B |
Open Firefox. |
Mod + G |
Open ChatGPT. |
Mod + T |
Toggle the light/dark theme. |
Mod + Escape |
Show or hide Waybar. |
Mod + Q |
Close the focused window. |
Mod + Shift + Q |
Power off the system. |
Mod + Shift + R |
Reload the Sway configuration. |
| Shortcut | Action |
|---|---|
Mod + A / Mod + F |
Focus the previous/next window. |
Mod + Shift + A / Mod + Shift + F |
Swap the focused window with the previous/next one. |
Mod + Ctrl + J / Mod + Ctrl + ; |
Shrink/grow the focused window's width by 50 px. |
Mod + Ctrl + K / Mod + Ctrl + L |
Shrink/grow the focused window's height by 50 px. |
Mod + S / Mod + D |
Focus the previous/next workspace. |
Mod + Shift + S / Mod + Shift + D |
Move the focused window to the previous/next workspace. |
Mod + J / Mod + ; |
Focus the output to the left/right. |
Mod + L / Mod + K |
Focus the output above/below. |
Mod + Shift + J / Mod + Shift + ; |
Move the focused window to the output on the left/right. |
Mod + Shift + L / Mod + Shift + K |
Move the focused window to the output above/below. |
| Shortcut | Action |
|---|---|
Print Screen |
Select a region, capture it, and open it in Swappy. |
Shift + Print Screen |
Capture the full screen and open it in Swappy. |
XF86AudioRaiseVolume / XF86AudioLowerVolume |
Increase/decrease audio volume. |
XF86AudioMute |
Toggle audio mute. |
XF86MonBrightnessUp / XF86MonBrightnessDown |
Increase/decrease display brightness. |
Workspaces are independent on each output and numbered consecutively from 1. Moving backward from the first workspace inserts a new first workspace and shifts the existing ones; empty workspaces are removed by Sway, then the remaining workspaces are renumbered immediately. Forward and backward navigation creates a workspace only when the current workspace contains a window, and moving a window never leaves an unnecessary empty workspace behind.
Window focus and swapping cycle through the windows on the focused workspace. A background Sway listener adjusts the next tiling split after each window opens or closes: it uses the longer screen axis for one or an odd number of windows, and the other axis for an even number, so the layout alternates automatically with the window count.