6 min read
Turning an Old Laptop into an Alpine Linux Pi-hole Server with Docker and Nightly Updates

I recently decided to repurpose an old laptop into a Pi-hole server to block ads across my home network. After some trial and error, I landed on a lightweight setup using Alpine Linux, Docker, and Docker Compose, with a nightly update script to keep everything fresh. Here’s how I did it—and how you can too!

Why Alpine and Docker?

I wanted something that wouldn’t tax the laptop’s aging hardware, so Alpine Linux was a no-brainer—it’s tiny, efficient, and perfect for a low-power device. Initially, I tried installing Pi-hole directly on Alpine, but the installer balked at Alpine’s apk package manager (it only likes apt or yum/dnf). After wrestling with the script, I pivoted to Docker, which made life way easier—Pi-hole runs in a container, and Alpine just hosts it.

Step 1: Setting Up Alpine Linux and Docker

First, I installed Alpine Linux on the laptop. Grab the ISO from alpinelinux.org, flash it to a USB with something like Rufus or dd, and boot it up. The setup-alpine script walks you through a basic install—set a root password, configure Ethernet, and pick a timezone. Don’t forget to press c during the repo section to enable the community repos. Otherwise, Docker and Docker Compose won’t be found when you install them via apk.

Next, I added Docker:

apk add docker docker-compose
rc-update add docker
rc-service docker start

This gets Docker and Docker Compose running on boot. Easy peasy.

To keep power usage low on the old hardware, I also installed cpufrequtils and set the CPU to powersave mode:

apk add cpufrequtils
cpufreq-set -g powersave

This dials back the CPU frequency, squeezing out extra efficiency—perfect for an always-on device.

Step 2: Deploying Pi-hole with Docker Compose

I created a docker-compose.yml file in a directory (say, /home/user/pihole) from the official docker doc:

# More info at https://github.com/pi-hole/docker-pi-hole/ and https://docs.pi-hole.net/
services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      # DNS Ports
      - "53:53/tcp"
      - "53:53/udp"
      # Default HTTP Port
      - "80:80/tcp"
      # Default HTTPs Port. FTL will generate a self-signed certificate
      - "443:443/tcp"
      # Uncomment the below if using Pi-hole as your DHCP Server
      #- "67:67/udp"
    environment:
      # Set the appropriate timezone for your location (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones), e.g:
      TZ: 'Europe/London'
      # Set a password to access the web interface. Not setting one will result in a random password being assigned
      FTLCONF_webserver_api_password: 'correct horse battery staple'
      # If using Docker's default `bridge` network setting the dns listening mode should be set to 'all'
      FTLCONF_dns_listeningMode: 'all'
    # Volumes store your data between container upgrades
    volumes:
      # For persisting Pi-hole's databases and common configuration file
      - './etc-pihole:/etc/pihole'
      # Uncomment the below if you have custom dnsmasq config files that you want to persist. Not needed for most starting fresh with Pi-hole v6. If you're upgrading from v5 you and have used this directory before, you should keep it enabled for the first v6 container start to allow for a complete migration. It can be removed afterwards. Needs environment variable FTLCONF_misc_etc_dnsmasq_d: 'true'
      #- './etc-dnsmasq.d:/etc/dnsmasq.d'
    cap_add:
      # See https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
      # Required if you are using Pi-hole as your DHCP server, else not needed
      - NET_ADMIN
      # Required if you are using Pi-hole as your NTP client to be able to set the host's system time
      - SYS_TIME
      # Optional, if Pi-hole should get some more processing time
      - SYS_NICE
    restart: unless-stopped

Then, I fired it up:

cd /home/user/pihole
docker compose up -d

Pi-hole started blocking ads! Check it at http://your-pihole-server-ip/admin with the password you set.

Step 3: Automating Updates and Reboots

To keep Alpine and Pi-hole updated, I wrote a script to run nightly. Here’s what I did:

Create the Update Script

To keep Alpine upgraded to the latest and greatest:

nano /etc/apk/repositories

Replace the original version repositories with these:

https://dl-cdn.alpinelinux.org/alpine/latest-stable/main
https://dl-cdn.alpinelinux.org/alpine/latest-stable/community

In /usr/local/bin/pihole-update.sh:

#!/bin/sh

# Update Alpine packages
apk update
apk upgrade -a

# Navigate to docker-compose directory
cd /home/user/pihole

# Pull latest Pi-hole image
docker compose pull

# Restart container with updated image
docker compose up -d

# Reboot the host
reboot

Make it executable:

chmod +x /usr/local/bin/pihole-update.sh

Test it:

/usr/local/bin/pihole-update.sh

It updates Alpine, refreshes Pi-hole, and reboots—sweet!

Schedule It with Cron (so it updates nightly)

Alpine uses crond for scheduling. Enable it:

rc-service crond start
rc-update add crond

Edit the crontab:

crontab -e

Add this line (runs at 1 AM to avoid clashing with Alpine’s 2 AM daily tasks):

0 1 * * * /usr/local/bin/pihole-update.sh

In vi, press i to insert, paste the line, hit Esc, then :wq to save. Check it:

crontab -l

You’ll see it alongside Alpine’s defaults:

# do daily/weekly/monthly maintenance
# min   hour    day     month   weekday command
*/15    *       *       *       *       run-parts /etc/periodic/15min
0       *       *       *       *       run-parts /etc/periodic/hourly
0       2       *       *       *       run-parts /etc/periodic/daily
0       3       *       *       6       run-parts /etc/periodic/weekly
0       5       1       *       *       run-parts /etc/periodic/monthly
0 1 * * * /usr/local/bin/pihole-update.sh

Lessons Learned

The bare-metal Pi-hole install on Alpine was a slog—script tweaks galore—but Docker saved the day. Enabling the community repos (c during setup) was key for Docker, and cpufrequtils helped keep power draw down. The update script keeps everything current without me lifting a finger, and the reboot ensures the old laptop stays spry.

Got an old laptop? Give this a shot! Tweak the timezone, paths, or timing to fit your setup. Questions? Drop ’em in the comments—I’d love to hear how it goes for you!