← ~/blog

Why I Self-Host Everything on Fedora CoreOS

The pitch

Most self-hosting guides start with "spin up an Ubuntu VM" or "install Docker on Debian." That works. But after running homelabs for years, I got tired of the same cycle: install a distro, configure it by hand, install Docker, hope nothing breaks on the next apt upgrade, and repeat when it inevitably does.

Fedora CoreOS changed that for me. It's an immutable operating system designed to run containers — and nothing else. No package manager surprises. No "I updated OpenSSL and now everything is broken." The OS updates itself automatically, and if an update goes wrong, it rolls back on the next boot. Your containers keep running.

If you've never heard of it, think of it this way: CoreOS is to a traditional Linux server what a smartphone OS is to a desktop. The intended workflow is to declare the state you want — users, services, firewall rules, container workloads — and let the system provision itself from that declaration. Every time. Identically. You can still SSH in and install packages manually for testing or debugging, but the idea is that your production config lives in a file, not in your shell history.

What makes it different

Immutable filesystem. The root filesystem is read-only. You can't accidentally rm -rf your way into a broken system. You can't install a package that conflicts with another. The OS layer is a single atomic image that gets replaced wholesale during updates, not patched file by file.

Automatic updates with rollback. CoreOS uses rpm-ostree and Zincati to download and stage updates in the background. On the next reboot, you're running the new version. If it fails to boot, it rolls back to the previous one. No babysitting, no maintenance windows.

Ignition and Butane. Instead of configuring a server by SSH-ing in and running commands, you write a Butane config — a YAML file that describes users, SSH keys, systemd services, files, and firewall rules. Butane compiles to Ignition JSON, which CoreOS consumes on first boot. Your entire server config is a single file you can version-control.

Podman and Quadlet. CoreOS ships with Podman (not Docker) and supports Quadlet — a way to define containers as systemd units. Your containers start on boot, restart on failure, and integrate with systemctl and journalctl like any other system service. No Docker daemon. No docker-compose up and hoping it stays up.

Who this is for

This series is aimed at homelabbers and self-hosters who are comfortable with Linux basics — you know what SSH is, you've used a terminal, you've maybe run a container or two. You don't need to be a sysadmin. You don't need to know what rpm-ostree is yet. I'll explain everything as we go.

If you're currently running a pile of containers on a regular Linux server and thinking "there has to be a better way," there is.

What we're building

Over the next 12 posts, I'm going to deploy a full homelab stack on Fedora CoreOS — one app at a time. Each post will include a working Butane config and Quadlet container files you can copy and adapt.

Here's the roadmap:

  1. Your first container on CoreOS — Uptime Kuma (monitoring dashboard)
  2. Run your own Git server — Gitea
  3. Replace Google Photos — Immich
  4. Self-host your passwords — Vaultwarden
  5. Your own cloud storage — Nextcloud
  6. Stream your media — Jellyfin
  7. Go paperless — Paperless-ngx
  8. Automate everything — n8n
  9. Block ads at DNS level — AdGuard Home
  10. Privacy-friendly analytics — Umami
  11. Reverse proxy with auto-TLS — Caddy
  12. Build a gym kiosk — a custom workout tracking app running as a locked-down kiosk

Each app is self-contained — you can follow along from post 1, or jump to whichever app interests you. By the end, you'll have a complete, production-grade homelab running on an OS that practically maintains itself.

Getting started

You'll need:

  • A machine to run CoreOS. A spare laptop, a mini-PC, a Proxmox VM — anything x86_64 with at least 2 cores and 2 GB RAM. An old ThinkPad from eBay works perfectly.
  • A USB drive (4 GB+) to flash the CoreOS installer.
  • An SSH key pair. If you don't have one, run ssh-keygen -t ed25519 on your workstation.

That's it. No paid licenses, no cloud accounts, no hardware shopping list. If you have a machine and a network cable, you're ready.

Installing Fedora CoreOS

Download the latest stable ISO from fedoraproject.org/coreos. You want the bare metal ISO if you're installing on physical hardware, or the QEMU image if you're using a VM.

Write a minimal Butane config to get started. Save this as config.bu:

variant: fcos
version: 1.5.0
passwd:
  users:
    - name: core
      ssh_authorized_keys:
        - ssh-ed25519 AAAA... your-public-key-here

This creates a user called core with your SSH key. That's the only thing you need for a first boot.

Compile it to Ignition:

podman run --interactive --rm \
  quay.io/coreos/butane:release \
  < config.bu > config.ign

Flash the ISO to USB, boot the machine from it, and install:

sudo coreos-installer install /dev/sda --ignition-file config.ign

Reboot, pull the USB, and SSH in:

ssh core@<machine-ip>

You're on CoreOS. No setup wizards, no package installation, no post-install checklist. The system is ready to run containers.

What you'll notice

A few things will feel different if you're coming from Ubuntu or Fedora Server:

  • No dnf or apt. If you need a package on the host (rare), you use rpm-ostree install. But 99% of the time, you run everything in containers.
  • No docker. Use podman — it's command-compatible (podman run, podman build, podman-compose), but daemonless. Containers run as regular processes.
  • Updates happen automatically. Within a day or two of booting, you'll see Zincati stage an update. On the next reboot, you're on the latest version. Check with rpm-ostree status.
  • Everything is in journalctl. Container logs, system services, boot messages — it's all in the journal. No more hunting through /var/log/.

Next up

In the next post, we'll deploy our first real app: Uptime Kuma — a lightweight monitoring dashboard. I'll walk through writing a Quadlet container file, opening firewall ports with Butane, and accessing the app from your local network.

If you want to get ahead, install CoreOS on a spare machine and make sure you can SSH in. That's all you need for post two.


This is post 1 of 13 in the Self-Hosting on Fedora CoreOS series.