I noticed some users asking for more detailed instructions for installing Pi-hole on a Raspberry Pi or another Linux setup.
As I was in the process of reinstalling both my trusty Pi 1B's with Raspbian Bookworm and the latest Pi-hole v6 release, and usually I document the steps, I thought why not share here.
Both Pi's have served Pi-hole reliable for over eight years now, still running on the original SD cards, and I think they have many years to come.
As its a headless setup, I'll be doing some of the configuring on my Linux laptop with an SD slot.
If you dont have a Linux system to make adjustments on the SD card boot partition, check out the source below:
https://www.raspberrypi.com/documentation/computers/configuration.html#remote-access
So we start with extracting the downloaded Raspbian lite image (32 bit armhf) from my NAS:
$ xz -cdk /mnt/nas_shared/Linux/Rasbian/armhf/2025-05-13-raspios-bookworm-armhf-lite.img.xz > 2025-05-13-raspios-bookworm-armhf-lite.img
$
List block devices to locate the SD card (disks and partitions):
$ lsblk -p
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
[..]
/dev/sdb 8:16 1 7.5G 0 disk
├─/dev/sdb1 8:17 1 256M 0 part /media/dehakkelaar/boot
└─/dev/sdb2 8:18 1 7.3G 0 part /media/dehakkelaar/rootfs
Unmount above two mounts first before flashing ... if mounted:
$ sudo umount /media/dehakkelaar/boot /media/dehakkelaar/rootfs
$
Flash the image to the SD card disk device found with above lsblk (be careful with the disk destroyer dd!):
$ sudo dd bs=8M status=progress if=2025-05-13-raspios-bookworm-armhf-lite.img of=/dev/sdb
2558525440 bytes (2.6 GB, 2.4 GiB) copied, 209 s, 12.2 MB/s
305+0 records in
305+0 records out
2558525440 bytes (2.6 GB, 2.4 GiB) copied, 280.718 s, 9.1 MB/s
List:
$ lsblk -p
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
[..]
/dev/sdb 8:16 1 7.5G 0 disk
├─/dev/sdb1 8:17 1 512M 0 part
└─/dev/sdb2 8:18 1 1.9G 0 part
Mount the boot partition (the small one) on a local tmp folder (create one if necessary):
$ sudo mount /dev/sdb1 /mnt/tmp
$
Touch to have sshd started at boot:
$ sudo touch /mnt/tmp/ssh
$
Create user dehakkelaar at boot time including a password hash:
$ sudo tee /mnt/tmp/userconf.txt <<< "dehakkelaar:$(openssl passwd -6)"
Password:
Verifying - Password:
dehakkelaar:$6$q2nk0ugbyrcfLNqN$Lc5WAku/YVOC3BPkbdsmuRvTeAn1V3qZKBqCAkChBTOjOHeFPklq0hOgWONZVq9n5Rl8EfRiPp/HtJ3wvixVW1
Unmount and sync cache before pulling the SD card:
$ sudo umount /mnt/tmp
$
$ sync
$
Boot the Pi from SD and connect via SSH.
First boot can take a long time before you can ping or SSH into it!
I didnt measure but it took a minute or maybe two before I could ping.
Maybe it has something to do with the root partition being resized to the full capacity of the SD card at first boot.
On the Pi, generate keys for SSH:
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/dehakkelaar/.ssh/id_rsa):
Created directory '/home/dehakkelaar/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/dehakkelaar/.ssh/id_rsa
Your public key has been saved in /home/dehakkelaar/.ssh/id_rsa.pub
Add trusted public SSH keys if have them:
nano ~/.ssh/authorized_keys
Reconfigure timezone:
sudo dpkg-reconfigure tzdata
Set the ph6a hostname:
$ sudo hostnamectl set-hostname ph6a
$
Also adjust the hostname in the hosts file including a static FQDN if want to:
$ sudo nano /etc/hosts
[..]
#127.0.1.1 raspberrypi
127.0.1.1 ph6a.home.dehakkelaar.nl ph6a
Check:
$ hostname
ph6a
$ hostname -f
ph6a.home.dehakkelaar.nl
$ dnsdomainname
home.dehakkelaar.nl
$ hostname -i
127.0.1.1
Disable some services with below one that are not necessary to be active on a dedicated Pi 1B (plus maybe Unbound).
alsa-restore.service is for sound cards;
avahi-daemon.service is an mDNS daemon for service discovery etc;
avahi-daemon.socket see above;
wpa_supplicant.service is necessary for WiFi connections;
triggerhappy.service is for "triggerhappy global hotkey daemon" keylogger;
triggerhappy.socket see above.
If in need of any of above ones, remove them from below list!
$ sudo systemctl disable --now alsa-restore.service avahi-daemon.service avahi-daemon.socket wpa_supplicant.service triggerhappy.service triggerhappy.socket
Removed "/etc/systemd/system/dbus-fi.w1.wpa_supplicant1.service".
Removed "/etc/systemd/system/dbus-org.freedesktop.Avahi.service".
Removed "/etc/systemd/system/sockets.target.wants/avahi-daemon.socket".
Removed "/etc/systemd/system/multi-user.target.wants/avahi-daemon.service".
Removed "/etc/systemd/system/multi-user.target.wants/wpa_supplicant.service".
Synchronizing state of triggerhappy.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable triggerhappy
Removed "/etc/systemd/system/sockets.target.wants/triggerhappy.socket".
Removed "/etc/systemd/system/multi-user.target.wants/triggerhappy.service".
Reload daemon failed: Refusing to reload, not enough space available on /run/systemd. Currently, 15.6M are free, but a safety buffer of 16.0M is enforced.
Steal some memory from the GPU and give it to the ARM core (default=64):
$ sudo tee -a /boot/firmware/config.txt <<< 'gpu_mem=16'
gpu_mem=16
Before:
$ vcgencmd get_mem gpu
gpu=64M
$ vcgencmd get_mem arm
arm=192M
After:
$ vcgencmd get_mem gpu
gpu=16M
$ vcgencmd get_mem arm
arm=240M
Add a useful ll alias for a long file list:
$ tee ~/.bash_aliases <<< "alias ll='ls -al --color'"
alias ll='ls -al --color'
$ . ~/.bash_aliases
$
Upgrade OS packages:
$ sudo apt update && sudo apt upgrade
[..]
60 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
Configure manual static IP details with below if you like.
Or if you want to have Pi-hole do DHCP services for your LAN.
sudo nmtui
Here is mine for IPv4 only:
$ nmcli -t -f name con show --active | xargs -d '\n' -n 1 nmcli -p -f ipv4.method,ipv4.addresses,ipv4.gateway,ipv4.dns,ipv4.dns-search connection show
===============================================================================
Connection profile details (Wired connection 1)
===============================================================================
ipv4.method: manual
-------------------------------------------------------------------------------
ipv4.addresses: 10.0.0.2/24
-------------------------------------------------------------------------------
ipv4.gateway: 10.0.0.1
-------------------------------------------------------------------------------
ipv4.dns: 10.0.0.1
-------------------------------------------------------------------------------
ipv4.dns-search: home.dehakkelaar.nl
-------------------------------------------------------------------------------
===============================================================================
Connection profile details (lo)
===============================================================================
ipv4.method: manual
-------------------------------------------------------------------------------
ipv4.addresses: 127.0.0.1/8
-------------------------------------------------------------------------------
ipv4.gateway: --
-------------------------------------------------------------------------------
ipv4.dns: --
-------------------------------------------------------------------------------
ipv4.dns-search: --
-------------------------------------------------------------------------------
Reboot to check if everything still works after the many changes:
sudo reboot
After reboot, check time syncing thats important for DNSSEC or Unbound:
$ timedatectl
Local time: Wed 2025-07-02 04:06:08 CEST
Universal time: Wed 2025-07-02 02:06:08 UTC
RTC time: n/a
Time zone: Europe/Amsterdam (CEST, +0200)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
$ timedatectl timesync-status
Server: 93.115.17.154 (2.debian.pool.ntp.org)
Poll interval: 1min 4s (min: 32s; max 34min 8s)
Leap: normal
Version: 4
Stratum: 1
Reference: PPS
Precision: 1us (-21)
Root distance: 1.174ms (max: 5s)
Offset: -810us
Delay: 19.371ms
Jitter: 306us
Packet count: 2
Frequency: +0.000ppm
Now you can follow the instructions in the docs for installing and configuring Pi-hole: