Blog

  • My 3-2-1 backup plan (and the gaps I found)

    Self-hosting means my photos, files and passwords live on my own hardware, which also means I’m responsible for not losing them. Here’s how my backups work today, the weak spot I’ve found, and the plan for my new 10 TB drive.

    The 3-2-1 rule

    The classic rule for backups that actually save you:

    • 3 copies of your data (the original plus two backups)
    • on 2 different devices or types of media
    • with 1 copy kept off-site

    Each part protects against something different: a failed disk, a corrupted backup, or a fire or burglary that takes out everything in one place.

    Where things stand today

    • Immich photos: backed up to an 8 TB external drive.
    • This blog: backed up nightly to the same drive (database plus site files, 14 days kept).
    • PCs and the Raspberry Pi: backed up daily to the same drive by UrBackup.
    • Vaultwarden: backed up nightly to a separate machine on my network, a Raspberry Pi, and to the 8 TB drive, with 30 days kept. Each backup is a consistent database snapshot that’s checked for corruption before it’s saved.
    • Nextcloud files: live on the 8 TB drive, and that’s currently their only copy.
    • Docker compose files: on the server’s system disk, without a separate copy yet.

    The weak spots

    The 8 TB drive was bought second-hand and arrived with some remapped sectors. A full surface scan found no new bad sectors developing, and Scrutiny now tracks its health every day, but it’s still a used drive carrying a lot of responsibility.

    More importantly, almost all of the backups sit in or next to the same box as the originals. A disk failure is covered, but a power surge, fire, theft or mistyped command could take out the originals and the backups together. The password vault is the exception, because its backups also go to the Pi. Everything else needs a copy that lives somewhere else. That’s what the new drive is for.

    The plan for the 10 TB drive

    Diagram of the 3-2-1 backup plan: the home server holds the live data (copy 1), nightly backups go to the 8 TB ExtStorage drive (copy 2), and a weekly full copy goes to a new 10 TB drive (copy 3) that is rotated off-site. Included: Immich photos and database, Nextcloud files, Vaultwarden vault, WordPress site, Docker compose files, and PC and Pi backups.
    Three copies, two devices, one off-site.
    1. Copy 3: a new 10 TB drive gets a full copy of everything important, refreshed regularly.
    2. Off-site: between updates, the drive lives away from the house, ideally with family. It’s unplugged, so ransomware or a bad command can’t reach it.
    3. Encrypted: an off-site drive can be lost or stolen, so the backup on it should be encrypted.

    Tools I’m considering

    • rsync: simple and reliable. It mirrors folders exactly, but a mirror also copies mistakes, such as deleted files.
    • restic: encrypted, deduplicated snapshots. You can go back to any earlier version, and it only stores the changes, so many snapshots fit on one drive. This is my likely pick.

    Whatever the tool, the most important step is testing a restore. I’ll regularly restore a few photos, a Nextcloud folder and the blog from the backup drive, and check they work.

    What I’ll include

    • The Immich photo library and its database dumps
    • Nextcloud data
    • The Vaultwarden data folder
    • WordPress backups
    • All Docker compose files and configs in /opt/stacks
    • The UrBackup store with the PC and Pi backups

    I’ll write a follow-up once it’s up and running, with the exact commands.

  • Scrutiny: catching failing hard drives before they die

    Hard drives rarely die without warning: they usually report trouble through S.M.A.R.T. long before they fail. The problem is that nobody reads those reports. Scrutiny reads them for me, and shows the health of every drive in my server on one simple web page.

    What is S.M.A.R.T.?

    Every modern hard drive and SSD keeps self-monitoring data: reallocated sectors, read errors, temperature, power-on hours, wear levels and more. Some of these numbers are strong predictors of failure. Scrutiny collects them on a schedule, stores the history, and flags drives as passed, warning or failed. Its thresholds are based on real-world failure rates published by Backblaze, rather than just the manufacturer’s own limits.

    Setting it up

    I use the “omnibus” image, which bundles the collector, web UI and database in one container. Here’s a compose file based on mine:

    services:
      scrutiny:
        container_name: scrutiny
        image: ghcr.io/analogj/scrutiny:master-omnibus
        restart: unless-stopped
        cap_add:
          - SYS_RAWIO
          - SYS_ADMIN
        ports:
          - "8085:8080"
        volumes:
          - /run/udev:/run/udev:ro
          - ./config:/opt/scrutiny/config
          - ./influxdb:/opt/scrutiny/influxdb
        devices:
          - /dev/sda
          - /dev/sdb
          - /dev/nvme0
    • devices lists every drive to monitor. Find yours with lsblk, and include all of them, especially the big data and backup drives you care about most.
    • SYS_RAWIO lets Scrutiny read S.M.A.R.T. data from SATA drives. SYS_ADMIN is needed for NVMe drives.
    • /run/udev lets it identify drives properly.
    • ./influxdb stores the history, so you can watch trends over time.

    Browse to http://<server-ip>:8085 and you’ll see a card for each drive.

    Reading the dashboard

    • Passed drives need nothing.
    • Warning means an attribute has crossed a threshold that’s linked to higher failure rates. Check that the drive is in your backups and keep an eye on it.
    • Failed means plan a replacement now, and make sure the data is backed up before anything else.

    Click a drive to see every attribute and its history. The ones I watch most are reallocated sector count, pending sectors and uncorrectable errors on hard drives, and “percentage used” on SSDs and NVMe drives.

    Notifications

    A dashboard you never open doesn’t help much. Scrutiny can send alerts through dozens of services (Discord, email, Telegram, ntfy and more) using notification URLs in config/scrutiny.yaml. Set one up so a failing drive actually tells you.

    Tips

    • USB enclosures don’t always pass S.M.A.R.T. data through. If an external drive shows no data, check Scrutiny’s docs for the right device type.
    • Scrutiny is not a backup. It gives you time to act, but drives can still fail suddenly. See my backup plan.
  • My home lab dashboard: Homarr and Glances

    Once you’re running more than a handful of self-hosted apps, remembering which port each one lives on gets old fast. Two tools fix that for me: Homarr, a start page with a tile for every service, and Glances, which shows how the server itself is coping.

    Diagram: a browser on the home network opens the Homarr dashboard, which has tiles for Immich, Nextcloud, Vaultwarden, Plex, Portainer and Technitium, and links to Glances, which reads CPU, memory, disk and Docker stats from the home server.
    Homarr gets me to every service; Glances shows the health of the server underneath.

    Homarr: the front door to everything

    Homarr is a self-hosted dashboard. You build it in the browser by dragging tiles around, with no config files to edit. Each tile links to one of your services and can show whether it’s up. Widgets can add extras like the weather, a calendar or stats from your apps.

    I set it as the home page on my browsers. Photos, files, passwords, Portainer, DNS: everything is one click away, and a red status dot tells me straight away if something has fallen over.

    Setting up Homarr

    Homarr needs a secret key to encrypt the credentials it stores for integrations. Generate one and put it in a .env file next to the compose file:

    mkdir -p /opt/stacks/homarr && cd /opt/stacks/homarr
    echo "SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32)" > .env

    Then compose.yaml:

    services:
      homarr:
        container_name: homarr
        image: ghcr.io/homarr-labs/homarr:latest
        restart: unless-stopped
        ports:
          - "7575:7575"
        environment:
          - SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY}
        volumes:
          - ./appdata:/appdata
          - /var/run/docker.sock:/var/run/docker.sock   # optional: lets Homarr see your containers
    docker compose up -d

    Open http://<your-server-ip>:7575, create your admin account, and start adding tiles. The Docker socket mount is optional. It lets Homarr list your containers and add them to the board quickly, but it also gives Homarr a lot of power over your server, so leave it out if you don’t need that.

    Keep it on your home network

    A dashboard is basically a map of your network, so I don’t expose mine to the internet. It goes through Nginx Proxy Manager with an access list that only allows local addresses, so I get a friendly name on my home network and it’s invisible from outside.

    Glances: is the server OK?

    Glances is a system monitor. Think top or htop, but showing everything at once: CPU, load, memory, network, disk activity, free space on every drive, temperatures, the busiest processes and every Docker container’s CPU and memory use. Run it in web mode and you get all of that in a browser tab.

    It’s my first stop when something feels slow. Is it Immich chewing through a big import? Is a drive filling up? Glances answers that in seconds.

    Setting up Glances

    This is my /opt/stacks/glances/compose.yaml:

    services:
      glances:
        container_name: glances
        image: nicolargo/glances:latest-full
        restart: unless-stopped
        pid: host
        network_mode: host
        environment:
          - GLANCES_OPT=-w -C /glances/conf/glances.conf
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock:ro
          - /etc/os-release:/etc/os-release:ro
          - ./glances.conf:/glances/conf/glances.conf:ro
          - /media/ExtStorage:/media/ExtStorage:ro

    A few lines are worth explaining, because they’re what let a container see the whole machine:

    • pid: host shows the server’s real processes, not just the container’s.
    • network_mode: host shows real network traffic, and serves the web UI on port 61208.
    • The read-only docker.sock adds per-container stats.
    • /etc/os-release shows the host’s OS name instead of the container’s.
    • Mount any extra drives you want to track (read-only), like my external storage drive.
    • -w starts web server mode.

    Running in a container, Glances also sees a lot of internal mounts that clutter the disk list. A small glances.conf hides them:

    [fs]
    hide=/etc/.*,/var/run/.*,/run/.*,/glances/.*

    Start it with docker compose up -d and browse to http://<your-server-ip>:61208.

    Security note: the Glances web UI has no login by default, and it shows a lot about your machine. Keep it on your home network, or start it with --password to require one.

    Putting them together

    Add a Glances tile to your Homarr board, and you have one page that gets you to every service and to the health of the server underneath. With more than one server, run the same Glances compose file on each and give each its own tile.

    New to Docker? Start with Getting started with Docker and Portainer.

  • Litchi: the app that makes older DJI drones better

    I recently bought Litchi, and it’s one of the best upgrades you can give an older DJI drone. It’s a third-party flight app that replaces DJI GO 4 and adds proper waypoint missions plus a set of autonomous flight modes, and it works with both my DJI Spark and Mavic Pro.

    What it does

    • Waypoint missions: Litchi’s headline feature. Plot a route of waypoints, set the height, speed and camera angle at each one, add points of interest for the camera to track, and add actions like taking a photo or starting video. The drone then flies the route smoothly, as many times as you like.
    • Orbit: circles a subject at a set radius and speed, for the classic reveal shot.
    • Focus: Litchi handles the yaw and gimbal to keep a subject in frame, so you only steer the drone’s position.
    • Track and Follow: keep a moving subject in shot, using computer vision or your phone’s GPS.
    • Panorama: automatically shoots the set of photos needed for a panorama.
    • VR mode: a split-screen view for phone VR goggles.
    • Flight logs: readable logs that can be uploaded to Airdata UAV to review flights and battery health.

    Plan missions on a big screen

    The best part is the Litchi Hub mission planner, which runs in a web browser on your PC or Mac. Plan the whole route at your desk with a satellite map, preview it, and save it to your Litchi account. Then open the app in the field and the mission is already there. You can also browse missions other pilots have shared.

    Diagram: plan a waypoint mission in Litchi Hub on a PC or Mac, sync it through your Litchi account, open it in the Litchi app on your phone with the DJI controller, then fly and film while keeping the drone in sight.
    Plan at your desk, fly in the field.

    Before your first mission

    • Check how heights are measured. Waypoint heights are relative to where you take off unless you set them to follow the ground. Over hills or valleys, that can put the drone far closer to the terrain, or far higher, than you expect.
    • Set what happens if the signal drops. Set missions to return home if the controller signal is lost, rather than carrying on without you.
    • The rules still apply. An autonomous mission is still your flight: stay under 120 m, keep the drone in sight the whole time, and keep away from people and restricted areas. See my post on the IAA’s rules for drone operators.
    • Start small. Fly a short, simple mission over open ground first, with your thumbs ready on the sticks to take over.

    Compatibility and price

    Litchi for DJI Drones is on Android and iOS and supports most of DJI’s older range, including the Spark, Mavic Pro, Mavic Air and many Phantoms. It does not support DJI’s newest drones, such as the Mini 3, Mini 4 and Mavic 3 Enterprise, so check the compatibility list before buying. It’s a one-off purchase with no subscription: around $25 on the App Store at the time of writing, with prices varying by region.

    Verdict

    For anyone flying an older DJI drone, Litchi is one of the best value upgrades going. It gives the Spark and Mavic Pro repeatable cinematic shots, and planning on a computer makes missions far easier to get right.

  • Watchtower: automatic Docker updates, done safely

    Keeping a dozen Docker containers up to date by hand gets tedious. Watchtower does it for me: every night it checks for new images, updates the containers that need it, and tells me what it did. The trick is knowing what not to let it update.

    What Watchtower does

    Watchtower runs as a container with access to the Docker socket. On a schedule, it checks the registry for a newer version of each container’s image. If there is one, it pulls it and recreates the container with exactly the same settings, then optionally removes the old image.

    My setup

    services:
      watchtower:
        image: containrrr/watchtower:latest
        container_name: watchtower
        restart: unless-stopped
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
        environment:
          - TZ=Europe/Dublin
          - WATCHTOWER_SCHEDULE=0 0 4 * * *
          - WATCHTOWER_CLEANUP=true
          - WATCHTOWER_INCLUDE_STOPPED=true
          - WATCHTOWER_REVIVE_STOPPED=false
          - WATCHTOWER_NOTIFICATIONS=shoutrrr
          - WATCHTOWER_NOTIFICATION_URL=discord://<token>@<webhook-id>
          - WATCHTOWER_NOTIFICATIONS_HOSTNAME=homeserver
    • WATCHTOWER_SCHEDULE is a cron expression with a seconds field first. 0 0 4 * * * means 4 am every day, when nobody’s using anything.
    • WATCHTOWER_CLEANUP deletes the old image after updating, so your disk doesn’t slowly fill up.
    • INCLUDE_STOPPED / REVIVE_STOPPED update stopped containers too, but leave them stopped.
    • Notifications go to a Discord channel through Shoutrrr, so each morning I can see what changed. Shoutrrr also supports email, Telegram, ntfy and others. Treat the notification URL like a password: anyone with it can post to your channel.

    If Watchtower starts failing with a Docker API version error after a Docker upgrade, setting DOCKER_API_VERSION to a version your Docker engine supports fixes it.

    Choosing what not to auto-update

    Automatic updates are great for simple apps, but some containers shouldn’t change without you watching. Watchtower is controlled with labels on each container:

    labels:
      # never update this container automatically
      - com.centurylinklabs.watchtower.enable=false
    
      # check for updates and notify me, but don't apply them
      - com.centurylinklabs.watchtower.monitor-only=true

    How I use them:

    • Databases (Postgres for Immich; MariaDB for Nextcloud, WordPress and Joplin) are either excluded or monitor-only. Database upgrades can need migration steps, and a surprise major version bump can break the app that depends on it.
    • Immich’s database and Redis are excluded completely, because Immich pins specific versions for a reason.
    • Vaultwarden is excluded. My password vault gets updated deliberately, after I’ve read the release notes.
    • Everything else, such as dashboards, monitoring tools and media servers, updates automatically.

    Is auto-updating a good idea?

    For a home lab, I think so, with these exclusions. Most updates are security and bug fixes, and getting them automatically beats forgetting for months. The rules that keep it safe:

    1. Exclude anything stateful or critical.
    2. Get notifications, so you know what changed if something breaks.
    3. Have backups, so a bad update is an inconvenience, not a disaster. See my backup plan.
  • DJI Super-Patcher: what it does, and why to be careful

    My DJI Spark and original Mavic Pro are getting on a bit, and DJI stopped updating their firmware years ago. That’s opened the door to community tools that modify it. The best known is DJI Super-Patcher. Here’s what it does, which features are genuinely useful, and why some of them are illegal to use here in Ireland and across the EU.

    What is DJI Super-Patcher?

    Super-Patcher is a Windows script that patches the flight controller module of older DJI drones. Under the hood it uses the community DJI_FC_Patcher project and the DUMLdore flashing tool. It wraps them in a menu-driven script, so you don’t have to edit firmware by hand.

    It only works on one specific stock firmware version per aircraft, and it leaves the rest of that firmware untouched. For my two drones, those versions are:

    • DJI Spark: V01.00.0900
    • Mavic Pro / Platinum / Alpine White: V01.04.0300

    It also supports several Phantom 4 models and the Inspire 2. Newer drones like the Mavic 2 and later aren’t supported.

    Diagram: the drone must be on the exact stock firmware; the Super-Patcher Windows script applies the chosen mods; the patched flight controller is flashed with DUMLdore; then the drone is tested in the simulator before any real flight. Re-flashing stock firmware undoes it.
    Super-Patcher only touches the flight controller module.

    What it can change

    The patch is modular. Some mods are always applied, and others are optional:

    • Galileo GPS: adds Europe’s satellite system for more satellites and potentially steadier positioning.
    • Slower yaw speed (optional): panning as slow as 1°/s, which gives noticeably smoother cinematic shots.
    • Waypoint distance: removes the 5 km waypoint mission limit.
    • Stealth mode (optional): turns off the rear LEDs, mainly useful to stop light reflecting into night shots.
    • Battery behaviour (optional): changes forced auto-landing, return-to-home calculations and low-battery warnings.
    • Altitude and no-fly zones: removes the height limit, the geo-zone (NFZ) restrictions and the airport limits.
    • FCC “boost” mode (optional, Mavic Pro and some others): switches the radio to the higher transmit power allowed in the US.

    The legal side (read this before you patch)

    The project itself is clear: removing a limit “does not mean it is safe or legal to do so in your area without proper authorization.” Here in Ireland, as everywhere in the EU, that matters a lot:

    • Height: in the EU Open category you must stay below 120 m above the ground, whatever the drone is capable of.
    • Geo-zones: airports, prisons and other restricted areas are legally restricted airspace. DJI’s NFZ database was just a helpful (and imperfect) reminder of them. Removing it doesn’t give you permission to fly there. Check the Irish Aviation Authority’s drone guidance and geo-zone maps before every flight.
    • FCC mode: transmitting at US power levels breaks European (CE) radio rules. Using it in Ireland or anywhere in the EU is illegal, so I’d leave this option off.
    • Registration and category: both the Spark (about 300 g) and the Mavic Pro have cameras and weigh over 250 g, so you need to register as an operator with the IAA and complete the online training. Neither drone has an EU class marking, so under the current rules they fly in the A3 subcategory: well away from people, and at least 150 m from residential, commercial and recreational areas.

    Rules change, so treat this as a summary, not legal advice, and check the IAA’s current guidance yourself.

    The technical risks

    • Bricking: flashing firmware always carries some risk. The project’s advice if anything goes wrong is to re-flash the stock firmware and redo the whole process.
    • Safety features: the battery mods can stop the drone landing itself when the battery is critically low. That’s how drones end up falling out of the sky.
    • Unmaintained: the last update to the project was in October 2024, and there are over 100 open issues. Don’t expect support if something goes wrong.
    • Warranty: long gone on drones this age, but modified firmware rules out any repair help from DJI.

    How the process works

    The project README has the full instructions, and you should follow those rather than a summary. In outline:

    1. Use a Windows PC (Windows 10 works best), and download the whole repository, keeping the folder structure intact.
    2. Make sure the drone is on the exact stock firmware listed above. The script can download and install it for you.
    3. Run the script, choose your optional mods (leave FCC off in the EU), and connect the drone when prompted. It takes around five minutes.
    4. Check the result in DJI GO 4, and try the drone in the app’s simulator before flying it for real.

    My take

    Super-Patcher is a clever piece of community work, and it gives older DJI drones some genuinely nice extras: Galileo, smoother yaw for video, and no more stray LED glow at night. But its headline features, removing height limits, no-fly zones and the CE radio limits, are exactly the ones you can’t legally use here. If you do patch your drone, leave FCC mode off, keep the battery safety features on, and fly by the same rules as an unpatched drone: under 120 m, out of restricted zones, and away from people.

  • Finding legal places to fly a drone around Cork

    “Where can I actually fly a drone around Cork?” It sounds like a simple question, but there’s no single list of approved spots. Whether a place is legal depends on the airspace, the land, the wildlife and the people around on the day. Here’s how I work it out.

    Step 1: check the airspace

    Cork has busy, controlled airspace. Cork Airport sits just south of the city, and there are restricted zones around other sensitive sites too. Before every flight:

    • Check the IAA’s official UAS geographical zones. Zones overlap, so read every one that covers your location.
    • Check for NOTAMs and temporary restricted areas. These pop up for events, emergencies and VIP visits.
    • Stay under 120 m, and keep the drone in sight.

    Step 2: check your drone’s subcategory

    My Spark and Mavic Pro are legacy drones over 250 g, so they fly in A3. That means no uninvolved people nearby, and at least 150 m from residential, commercial, industrial and recreational areas. That rules out a lot: towns and villages, busy beaches, sports pitches and parks. (Full details in what the IAA requires.)

    Step 3: check the land

    Flying and taking off are two different things. You need the right to be on the land you take off from.

    • Private farmland: ask the landowner. Most are fine if you ask politely and keep away from livestock.
    • Forests: many are managed by Coillte. Check their current policy and get permission before flying from their land.
    • National parks and nature reserves: run by the NPWS, and they generally need permission.
    • Heritage sites: OPW sites, such as forts and historic buildings, generally don’t allow drones without permission.

    Step 4: think about wildlife

    Cork Harbour and much of the coastline are important for birds, and parts are protected as Special Protection Areas. Drones can panic nesting and feeding birds. Avoid cliffs and colonies in nesting season, and if birds react to the drone, leave.

    What tends to work

    Putting that together, the places that usually tick every box are:

    • Open farmland and hills inland, with the landowner’s permission, well away from houses and roads.
    • Quiet stretches of coastline away from towns, ideally out of season and early in the morning, when there’s nobody about.
    • Model aircraft flying fields, if you join a local club.

    I’m deliberately not publishing exact locations. Conditions change, and what was quiet and legal last month might not be today. Check every time, and if in doubt, don’t fly.

  • NCT preparation checklist: what to check at home

    Most NCT failures are for simple things you could have spotted and fixed at home in ten minutes: a dead bulb, a worn wiper or a warning light. Here’s a checklist to run through before your NCT.

    How the NCT works

    The National Car Test is Ireland’s roadworthiness test. Cars are first tested at four years old, then every two years, and every year once they’re over ten years old. Book at ncts.ie, and check the site for what to bring on the day.

    Lights

    • Headlights: dipped and main beam, both sides
    • Sidelights and rear lights
    • Brake lights, including the high-level centre one
    • Indicators, front, rear and side repeaters, plus hazards
    • Rear fog light
    • Reverse light
    • Number plate lights
    • Headlight lenses clear, not cracked or cloudy

    Get someone to stand behind the car while you press the brake, or reverse up to a garage door or window and watch the reflection.

    Tyres and wheels

    • Tread depth at least 1.6 mm across the tread. More is far safer, especially in the wet.
    • No cuts, bulges or exposed cords, including on the inner sidewalls
    • Matching size and type on each axle
    • Wheel nuts all present

    Visibility

    • Windscreen free of chips and cracks, especially in the area the wipers clear in front of the driver
    • Wipers clear the screen without smearing. Blades are cheap, so replace them if in doubt.
    • Washers spray properly and the bottle is full
    • Mirrors present, secure and not cracked

    Inside the car

    • No warning lights on the dash once the engine is running: engine, ABS, airbag and anything else. Cars like my C4 Grand Picasso have an electronic parking brake, and a fault light for it counts too.
    • Horn works
    • All seatbelts latch, release and retract properly
    • Doors open from inside and outside
    • Seats secure

    Brakes, steering and suspension

    • Brakes pull the car up straight, with no grinding or pulling to one side
    • Parking brake holds the car on a slope
    • No knocking over bumps, which often means worn drop links or bushes
    • No excessive play in the steering

    Under the car and bonnet

    • No fluid leaks: oil, coolant, brake fluid or power steering fluid
    • Exhaust secure, with no blowing or leaks
    • Fluid levels topped up

    Emissions (diesels)

    Diesels get a smoke test. Give the car a good long run before the test (20 to 30 minutes on a motorway or open road), so the engine is fully warm and the DPF has had a chance to clean itself. Arrive with the engine hot. A car that’s only done short town trips for weeks is far more likely to struggle.

    Paperwork and plates

    • Number plates readable, correct format, and not cracked or faded
    • Bring what the NCTS asks for when you book

    If it fails

    Don’t panic. You get a list of what failed, and you can usually fix the simple items yourself and book a re-test. Many of the most common failure items, such as bulbs, wipers and drop links, are easy DIY jobs with a basic tool kit.

  • UrBackup: backing up every computer in the house

    My home server doesn’t just back up itself. It also backs up the other computers in the house. UrBackup is the open-source server that does it, quietly, every day, with no subscription.

    What UrBackup does

    UrBackup is a client/server backup system. You run the server on one machine, install a small client on each computer you want protected, and the server pulls backups over the network on a schedule:

    • File backups: incremental and full copies of chosen folders, with deduplication so unchanged files aren’t stored twice.
    • Image backups (Windows): a full disk image you can restore a whole PC from, even onto a new drive.
    • A web interface for status, settings and browsing or downloading backed-up files.

    I use it to back up my Windows PC and a Raspberry Pi, with everything stored on the large external drive in my server.

    My setup

    services:
      urbackup:
        image: uroni/urbackup-server:latest
        container_name: urbackup
        restart: unless-stopped
        network_mode: host
        environment:
          - PUID=1000
          - PGID=1000
          - TZ=Europe/Dublin
        volumes:
          - /opt/stacks/urbackup/data:/var/urbackup
          - /media/ExtStorage/backups/urbackup:/backups
    • network_mode: host lets clients on your network discover the server automatically, using UDP broadcasts.
    • /backups is where the backups themselves go. Point it at a big disk.
    • /var/urbackup holds the server’s own database and settings. Keep it on a reliable disk too.

    The web interface runs on port 55414: http://<server-ip>:55414.

    Adding a computer

    1. Download the client for Windows, Linux or macOS from the UrBackup website and install it.
    2. On the same network, the client finds the server by itself, and it appears in the web interface within a minute or two.
    3. In the client (Windows tray icon), choose which folders to back up, or let it use the defaults.
    4. Adjust the schedule and how many backups to keep under Settings in the web interface.

    Restoring

    • Single files: browse the backups in the web interface and download what you need, or restore from the client.
    • A whole Windows PC: boot from the UrBackup restore image on a USB stick and restore an image backup to the disk.

    Test a restore now and then. A backup you’ve never restored from is only a hope.

    One important caveat

    UrBackup stores its backups on my server, so if something happens to the server itself, the PC backups go with it. That’s why they’re part of my 3-2-1 backup plan, which copies everything important to a second drive kept away from the house.

  • Replacing Pi-hole with Technitium DNS, on two servers

    For a long time I ran Pi-hole as my network’s ad and malware sink. It’s a great project, but I’ve since replaced it with Technitium DNS Server, running on two machines so that one going down never takes the whole house offline.

    What a DNS sinkhole does

    Every time a device opens a website or an app phones home, it first asks a DNS server to turn a name like ads.example.com into an IP address. A DNS sinkhole checks each name against blocklists of known ad, tracking and malware domains. If the name is on a list, it simply doesn’t give a real answer, so the ad, tracker or malicious download never loads. This works for every device on the network, including smart TVs and phones where you can’t install an ad blocker.

    Why I moved from Pi-hole to Technitium

    Pi-hole does blocking brilliantly. Technitium does blocking too, but it’s also a complete DNS server, which suits a growing home lab:

    • Encrypted upstream DNS built in: queries leave my network over DNS-over-HTTPS, with no extra resolver containers to bolt on.
    • Wildcard blocklists: blocking a domain blocks all its subdomains, so modern lists stay smaller and more effective.
    • Proper local zones: I can give my own machines and services real names on the home network.
    • Apps: optional add-ons like a searchable query log stored in SQLite.
    • A clean web interface with a dashboard of queries, blocked requests and top clients.

    Why two servers?

    When all your DNS goes through one box, that box becomes a single point of failure. Reboot it for updates, or have it crash, and every device in the house “loses the internet”, even though the connection is fine. So I run Technitium on two separate machines, and the router hands out both as DNS 1 and DNS 2.

    Diagram: every device is given two DNS servers, Technitium 1 and Technitium 2; both check blocklists for ads, trackers and malware, giving no answer for blocked domains and forwarding allowed queries upstream to Cloudflare and Google over DNS-over-HTTPS.
    Two Technitium servers share the load and cover for each other.

    Setting it up

    1. Free up port 53 (Ubuntu)

    Ubuntu’s built-in systemd-resolved already listens on port 53, which a DNS server needs. Turn off its stub listener:

    sudo sed -i 's/^#\?DNSStubListener=.*/DNSStubListener=no/' /etc/systemd/resolved.conf
    sudo systemctl restart systemd-resolved

    Once Technitium is running, point the server at itself (127.0.0.1) and at your second DNS server, so it keeps working even if the local one is down.

    2. The compose file

    Save as /opt/stacks/technitium/compose.yaml, and put the admin password in a .env file rather than in the compose file:

    services:
      technitium:
        container_name: technitium
        image: technitium/dns-server:latest
        network_mode: host
        environment:
          TZ: 'Europe/Dublin'
          DNS_SERVER_DOMAIN: 'dns-server'
          DNS_SERVER_ADMIN_PASSWORD: ${DNS_ADMIN_PASSWORD}
          DNS_SERVER_LOG_USING_LOCAL_TIME: 'true'
          DNS_SERVER_WEB_SERVICE_HTTP_PORT: '5380'
          DNS_SERVER_WEB_SERVICE_LOCAL_ADDRESSES: '0.0.0.0'
          DNS_SERVER_ENABLE_BLOCKING: 'true'
        volumes:
          - ./etc-technitium:/etc/dns
          - ./logs:/var/log/technitium
        cap_add:
          - NET_BIND_SERVICE
        restart: unless-stopped
    echo "DNS_ADMIN_PASSWORD=pick-a-strong-password" > .env
    chmod 600 .env
    docker compose up -d

    network_mode: host lets Technitium answer DNS on port 53 directly, and see real client IPs in its logs. The password variable only sets the admin password on first start. After that, change it in the web UI.

    3. Encrypted upstream DNS

    Open http://<server-ip>:5380 and log in. Under Settings → Proxy & Forwarders, use the quick-select to add Cloudflare and Google with the DNS-over-HTTPS protocol. I use both:

    https://1.1.1.1/dns-query
    https://8.8.8.8/dns-query

    4. Add blocklists

    Under Settings → Blocking, make sure blocking is enabled and add your blocklist URLs. These are the ones I use:

    • HaGeZi Multi and HaGeZi Pro++: well-maintained lists covering ads, tracking, malware, phishing and scams, in wildcard format (github.com/hagezi/dns-blocklists).
    • StevenBlack unified hosts with the fake-news and gambling extensions (github.com/StevenBlack/hosts). There’s also a variant that adds adult content, which is handy on a family network.

    Technitium downloads and refreshes the lists automatically. Start with one or two lists and add more gradually. Aggressive lists like Pro++ occasionally block something you need, which the Allowed tab fixes in seconds.

    5. Build the second server

    On the second machine, repeat steps 1 and 2. Then use Settings → Backup Settings on the first server and Restore Settings on the second to copy the forwarders, blocklists and allow-list across.

    Keep the two in sync. Devices don’t strictly use “DNS 1 first, DNS 2 as backup”. They often spread queries across both. If the servers have different blocklists, ads will mysteriously appear on some page loads and not others. Whenever you change a list on one, make the same change on the other.

    6. Point your network at them

    In your router’s DHCP settings, set the primary DNS to server 1 and the secondary to server 2. Devices pick up the change the next time they renew their lease, or straight away if you reconnect them.

    7. Test it

    # a normal domain should resolve on both servers
    dig +short @<server-1-ip> example.com
    dig +short @<server-2-ip> example.com
    
    # an ad domain should come back empty (blocked)
    dig +short @<server-1-ip> doubleclick.net

    Then the real test: stop Technitium on one server and keep browsing. If everything still loads, your redundancy works.

    Was it worth switching?

    Yes. Ads and known-bad domains are blocked for every device in the house, DNS queries leave the network encrypted, and rebooting a server for updates no longer means everyone shouting that the Wi-Fi is broken.

    New to Docker? Start with Getting started with Docker and Portainer.