Blog

  • Kopia Is the Backup Tool I’ve Been Looking For

    I’ve tried a lot of backup tools over the years. rsync scripts held together with cron jobs and optimism. Duplicati, which I wanted to love but never quite trusted after it silently failed on me one too many times. Restic, which is solid but feels like it expects you to be a sysadmin for breakfast. And then I found Kopia, and something clicked.

    Kopia is a free, open source backup tool — but that description really undersells it. It’s one of those pieces of software where you can tell that the people who built it actually thought hard about the problem before writing any code.

    The Core Idea Is Elegant

    Kopia works on a concept called a repository. When you back up, your data doesn’t just get copied somewhere — it gets split into chunks, compressed, encrypted, and stored by the hash of its content. That last part is key: if the same chunk of data exists in a hundred different files across a hundred different snapshots, it gets stored exactly once. This is content-addressable, deduplicated storage, and it means your backups are both space-efficient and fast after that first run.

    The encryption happens client-side, before anything leaves your machine. The storage backend — whether that’s a cloud provider, a NAS, an external drive, or a remote server — sees only opaque encrypted blobs. It doesn’t matter if the storage is “trusted” or not. Your data is yours, and only you can read it.

    Every backup run creates a snapshot: a complete, point-in-time picture of your data. You can have hundreds of them. Browse them. Mount them as a filesystem and reach into them like a time machine. Restore a single file from six months ago without extracting a giant archive. It’s genuinely one of those things that makes you wonder why not every backup tool works this way.

    It Goes Everywhere

    Kopia supports an almost absurd number of storage backends: local filesystem, SFTP, Amazon S3, Backblaze B2, Google Cloud Storage, Azure Blob, Wasabi, Cloudflare R2, MinIO — anything S3-compatible, really. And if somehow that’s not enough, there’s an Rclone backend that gives you access to 40+ more providers. The point is: wherever you want your backups to live, Kopia can put them there.

    This matters because a good backup strategy is a 3-2-1 — three copies, on two different media, with one offsite. Kopia makes that genuinely easy. Local repo on an external drive for fast restores. Cloud repo on something like Backblaze B2 for offsite peace of mind. Both encrypted, both deduplicated, both managed with the same tool and the same mental model.

    The Maintenance Just… Happens

    One of the things that killed my trust in other tools was the overhead of keeping them healthy. Restic requires you to remember to run prune and check or your repository slowly bloats. Duplicati’s database would occasionally get into a state that required manual surgery. Kopia handles repository maintenance automatically. You set a retention policy — keep this many hourly, daily, weekly, monthly snapshots — and Kopia takes care of the rest. Garbage collection, compaction, index maintenance. It just runs.

    There’s a Real GUI

    Kopia has a full desktop application called KopiaUI, and it’s not an afterthought. Repository setup wizards, a snapshot browser, policy management, scheduling, a live progress view — it’s all there. Under the hood it’s running the same Kopia server process as the CLI, so you’re not using a dumbed-down version of anything. You get the full power of the tool in a proper interface, and you can drop into the CLI whenever you want without losing anything.

    I mention this because it matters for adoption. A backup tool only works if people actually use it. Having a GUI that doesn’t make you feel like you need a computer science degree means you can actually recommend Kopia to people who aren’t already comfortable in a terminal.

    It’s Fast. Really Fast.

    Kopia is written in Go and it shows. The deduplication, hashing, and compression pipeline is parallelised aggressively. After the initial backup, incremental snapshots are genuinely quick — often just a handful of seconds for a busy home directory, because only changed chunks get processed and uploaded. I’ve thrown large repositories at it and it doesn’t flinch.

    The Project Is Alive

    Open source backup tools have a habit of going quiet right when you need them most. Kopia’s GitHub repository is actively developed, issues get responses, releases come regularly, and there’s a real community around it. That matters a lot when you’re trusting a tool with your most important data.

    The Thing About Backups

    The honest truth about backups is that the best backup tool is the one you’ll actually set up and leave running. And for the first time, I have one that I trust completely, that doesn’t require babysitting, that works with whatever storage I throw at it, and that I genuinely enjoy using. That’s rare.

    If you’re not backing up, or you’re backing up and not really sure if it works, go look at kopia.io. Seriously. The day you need your backups is not the day you want to find out they weren’t working.

  • Getting the Camera Working on a ThinkPad X9-15 Gen 1 (Ubuntu 24.04)

    So I bought a Lenovo ThinkPad X9-15 Gen 1. Beautiful machine. Fast. Thin. Running Ubuntu 24.04 LTS like a dream — except for one thing: the webcam was completely dead. Black screen. No device. Nothing.

    Turns out Intel decided that standard USB webcams are too easy, and built a brand-new MIPI camera architecture into this laptop that needs a completely non-standard software stack just to produce a mediocre 30fps image. Fantastic. Here’s the full story of what it took to get it working.

    Why the Camera Doesn’t Work Out of the Box

    The X9-15 Gen 1 (model 21Q6) uses a Sony IMX471 sensor wired through an Intel IPU7 (Image Processing Unit) and an IVSC (Intel Visual Sensing Controller) privacy bridge. This is nothing like a normal USB webcam, which just appears as /dev/video0 and works with any app.

    The full data path looks like this:

    IMX471 sensor
         |
    I²C/CSI lanes
         |
    IVSC (privacy controller)
         |
    Intel IPU7 (PCI device)
         |
    v4l2 media graph (subdevs + capture nodes)
         |
    libcamhal (Intel proprietary userspace HAL)
         |
    gstreamer icamerasrc
         |
    v4l2-relayd (pumps the stream into a v4l2loopback device)
         |
    /dev/video0  ← what your apps actually see

    Every single layer needs to be present and correctly configured. If one piece is missing or loads in the wrong order, the camera fails silently. And by default, most of these pieces are either absent or misconfigured on a standard Ubuntu install.

    The Standard Recipe — Necessary but Not Sufficient

    The official starting point that most guides point to goes like this:

    1. Install the OEM meta package: oem-sutton-dana-meta
    2. Add the Lenovo Canonical archive repository
    3. Install ubuntu-oem-keyring and libcamhal-ipu7x
    4. Reboot

    I ran this. Rebooted. Camera still showed a black screen, then the privacy LED briefly flickered, and the camera app closed itself. Progress? Kind of. Actually no.

    Debugging: Layer by Layer

    The HAL Knew Nothing About the Sensor

    Running gst-inspect-1.0 icamerasrc revealed the first real clue:

    CamHAL: getAvailableSensors, Found IPU: IPU7
    CamHAL: parseSensors: No sensors available

    The HAL knew it was on an IPU7 system, but found zero sensors. The IMX471 was invisible to it.

    PipeWire Was Also Complaining

    The WirePlumber log had this gem:

    SPA handle 'api.libcamera.enum.manager' could not be loaded; is it installed?

    Fix: install libspa-0.2-libcamera. This didn’t fix the black screen directly, but it was needed for PipeWire to properly enumerate libcamera devices later down the line.

    Missing Kernel Module Packages

    Here’s where it gets annoying. The oem-sutton-dana-meta package recommends — but does not depend on — several critical kernel module packages. By default, apt doesn’t install Recommends in all configurations, and even when it does, some were simply absent:

    • linux-modules-vision-oem-24.04d — contains ivsc_csi and ivsc_ace, the IVSC bridge drivers
    • linux-modules-usbio-oem-24.04d — some sensors need this

    Installing them helped, but the IVSC modules still didn’t auto-load because no hardware match fired on boot. Manual modprobe ivsc_csi finally got the HAL to advance:

    CamHAL: parseSensors: I will Load config file: sensors/imx471-uf.json
    CamHAL: parseSensors, sensors/imx471-uf.json loaded!

    So the sensor profile was already on disk (installed by libcamhal-ipu7x-common). The problem was driver bind and module load order — not missing HAL config.

    The IMX471 Driver Lives in the IPU6 Package

    This is the most baffling thing I encountered. Searching for the sensor driver:

    apt-file search imx471.ko

    Result:

    linux-modules-ipu6-6.17.0-1017-oem: /lib/modules/.../ipu6/imx471.ko.zst

    The IMX471 driver — for an IPU7 laptop — is packaged inside linux-modules-ipu6-oem-24.04d. Canonical packaged all the i2c sensor drivers (imx*, ov*, hi*, etc.) in the IPU6 modules package, regardless of which IPU generation the laptop actually uses. Install the wrong generation’s modules, and you simply have no sensor driver at all.

    After installing linux-modules-ipu6-oem-24.04d and rebooting: the privacy LED lit up when the camera app opened. The sensor was finally being powered on! But the app still went black and crashed. Getting closer.

    Module Load Order — The Final Boss

    The kernel logs were now showing this, on repeat:

    imx471 i2c-SONY471A:00: Start streaming
    imx471 i2c-SONY471A:00: imx471 power off

    The driver was binding at the i2c layer, but the v4l2 media link was never established. The reason: ipu_bridge (the component that stitches the sensor into the IPU7 media graph) does its enumeration at probe time. If imx471, ivsc_csi, and ivsc_ace aren’t already loaded when intel_ipu7 probes, the bridge moves on and the window closes. Any modules that load later get bound at the i2c layer but never wired into the media graph.

    The fix: force all three modules to load early, before the IPU7 driver probes, by adding them to /etc/modules-load.d/ and to the initramfs:

    # /etc/modules-load.d/ipu7-camera.conf
    ivsc_ace
    ivsc_csi
    imx471

    Then rebuild the initramfs with sudo update-initramfs -u and reboot.

    The Camera Was Working — the Test App Wasn’t

    After all of the above, I was still seeing a black screen in the GNOME camera app. I spent more time staring at HAL log errors before realising I should just test in a real application. Opened Google Meet.

    It worked immediately.

    The GNOME camera app simply doesn’t handle the v4l2loopback device correctly on this stack. It opens it, fails to negotiate a format, and exits. Browser-based apps and anything that goes through PipeWire work fine. Half my debugging time after step 6 was wasted staring at errors from a broken test tool, not a broken camera.

    Oh, and It Was Upside Down

    The IMX471 is physically mounted inverted in the X9-15 chassis. The image was rotated 180 degrees. Not ideal for video calls.

    The fix lives in /etc/v4l2-relayd.d/default.confnot in /etc/default/v4l2-relayd (that file is ignored by the actual running process) and not in a systemd drop-in override (the base unit is a oneshot /bin/true). The real config is read by a systemd generator that creates per-file service instances:

    # /etc/v4l2-relayd.d/default.conf
    VIDEOSRC=icamerasrc buffer-count=7 ! videoflip method=rotate-180

    Restart v4l2-relayd@default.service and the image is the right way up.

    What Was Actually Missing

    To summarise what a vanilla post-install with the standard recipe was missing:

    Missing piecePackage / fix
    IMX471 sensor driverlinux-modules-ipu6-oem-24.04d (yes, IPU6 on an IPU7 machine)
    IVSC bridge driverlinux-modules-vision-oem-24.04d
    USBIO driverlinux-modules-usbio-oem-24.04d
    PipeWire libcamera SPAlibspa-0.2-libcamera
    Early module load order/etc/modules-load.d/ipu7-camera.conf + initramfs
    Upside-down imagevideoflip method=rotate-180 in /etc/v4l2-relayd.d/default.conf

    The Complete Fix Script

    If you just want to run one script and be done with it, here it is. Run it as your normal user (it will sudo when needed), then reboot once.

    #!/usr/bin/env bash
    # Complete camera fix for Lenovo ThinkPad X9-15 Gen 1 (21Q6) on Ubuntu 24.04.
    # Run once, then reboot.
    
    set -euo pipefail
    
    if [ "$(id -u)" -eq 0 ]; then
        echo "Run as your normal user (script will sudo when needed)." >&2
        exit 1
    fi
    
    echo "==> [1/7] System update..."
    sudo apt update
    sudo apt upgrade -y
    
    echo "==> [2/7] Lenovo OEM metapackage + archive..."
    sudo apt install -y oem-sutton-dana-meta
    if ! grep -rq 'lenovo.archive.canonical.com' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null; then
        sudo add-apt-repository -y "deb http://lenovo.archive.canonical.com/ noble sutton"
    fi
    sudo apt install -y ubuntu-oem-keyring
    sudo apt update
    
    echo "==> [3/7] IPU7 camera HAL + sensor drivers + PipeWire libcamera SPA..."
    sudo apt install -y \
        libcamhal-ipu7x \
        libspa-0.2-libcamera \
        linux-modules-ipu7-oem-24.04d \
        linux-modules-ipu6-oem-24.04d \
        linux-modules-vision-oem-24.04d \
        linux-modules-usbio-oem-24.04d
    
    echo "==> [4/7] Force IVSC + IMX471 to load early at boot..."
    sudo tee /etc/modules-load.d/ipu7-camera.conf >/dev/null <<'EOF'
    ivsc_ace
    ivsc_csi
    imx471
    EOF
    
    echo "==> [5/7] Add modules to initramfs..."
    INITRAMFS_TARGET=/etc/initramfs-tools/modules
    if [ -d /etc/initramfs-tools/modules.d ]; then
        sudo tee /etc/initramfs-tools/modules.d/ipu7-camera >/dev/null <<'EOF'
    ivsc_ace
    ivsc_csi
    imx471
    EOF
    else
        for m in ivsc_ace ivsc_csi imx471; do
            grep -qx "$m" "$INITRAMFS_TARGET" 2>/dev/null || echo "$m" | sudo tee -a "$INITRAMFS_TARGET" >/dev/null
        done
    fi
    
    echo "==> [6/7] Configure v4l2-relayd to flip 180 (camera is physically inverted)..."
    DEFAULT_CONF=/etc/v4l2-relayd.d/default.conf
    if [ -f "$DEFAULT_CONF" ] && ! grep -q 'videoflip' "$DEFAULT_CONF"; then
        sudo cp "$DEFAULT_CONF" "${DEFAULT_CONF}.bak"
        sudo sed -i 's|^VIDEOSRC=.*|VIDEOSRC=icamerasrc buffer-count=7 ! videoflip method=rotate-180|' "$DEFAULT_CONF"
    fi
    
    echo "==> [7/7] Rebuild initramfs..."
    sudo update-initramfs -u
    
    echo
    echo "All done. Reboot now, then test in Google Meet or a browser — not the GNOME camera app."
    echo "Note: 30 fps is the hardware cap for this sensor."

    Two Gotchas Worth Highlighting

    Don’t test with the GNOME camera app. It fails on this v4l2loopback stack. Use Google Meet, Microsoft Teams, or any browser-based video call. Those work correctly.

    30 fps is the ceiling. The IMX471’s HAL profile only declares 15 fps and 30 fps modes. The sensor itself is an 8MP stills sensor — its video modes top out at 30 fps. Don’t bother trying to push it higher.

    References