Skip to content

2026/01/24 - LAN Bitwarden + Pi-hole on a PoE Raspberry Pi (Docker)

This post documents my full setup of Bitwarden Lite and Pi-hole on a PoE Raspberry Pi connected to a switch, so it’s always-on. The objective was to keep everything LAN-only, use friendly internal DNS names like bitwarden.home.arpa, and avoid exposing anything publicly.

Hardware and environment used:

  • Raspberry Pi (PoE, always on)
  • Debian-based OS on the Pi
  • Docker + Docker Compose v2
  • Router: Virgin Media Hub 4
  • LAN IP for the Pi: 192.168.*.***
  • Fedora workstation used for testing

1. Installing Docker Compose properly (and avoiding a classic trap)

I already had Docker installed and working:

docker --version
````

I attempted to install an old `docker-compose` binary manually using a GitHub release link:

```bash
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" \
  -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version

This failed with:

/usr/local/bin/docker-compose: line 1: Not: command not found

The download was only 9 bytes, which strongly indicates GitHub returned a Not Found response instead of a binary. Lesson learned:

Do not install old docker-compose binaries manually unless you verify the URL and architecture.

Correct fix: Docker Compose v2 plugin

Modern Docker uses the Compose plugin (docker compose, space not dash):

sudo rm -f /usr/local/bin/docker-compose
sudo apt-get update
sudo apt-get install -y docker-compose-plugin
docker compose version

2. Bitwarden: don’t clone bitwarden/server

I initially cloned Bitwarden’s server repo:

mkdir ~/bitwarden
cd ~/bitwarden
git clone https://github.com/bitwarden/server.git .
docker compose up -d

This failed with:

no configuration file provided: not found

Cloning bitwarden/server is not the normal self-host method. Instead, for ARM homelabs, the cleanest approach is:

Bitwarden Lite (container-first, simple, supports homelab use)

So I removed the wrong repo:

rm -rf ~/bitwarden

3. Deploy Bitwarden Lite with Docker Compose

Create a working directory:

mkdir -p ~/bitwarden-lite
cd ~/bitwarden-lite

Create settings.env (this is important):

BW_DOMAIN=bitwarden.home.arpa
BW_INSTALLATION_ID=<your real ID>
BW_INSTALLATION_KEY=<your real key>
BW_DB_PROVIDER=sqlite

Bitwarden Lite requires a real Installation ID and Installation Key obtained from Bitwarden’s hosting page. Placeholders will cause backend crashes and registration failures.

Create docker-compose.yml:

services:
  bitwarden:
    image: ghcr.io/bitwarden/lite
    restart: always
    env_file:
      - settings.env
    volumes:
      - bwdata:/etc/bitwarden
    ports:
      - "8080:8080"

volumes:
  bwdata:

Start the container:

docker compose up -d
docker ps

Bitwarden Lite is now reachable on:

  • http://192.168.*.***:8080

4. Choosing a DNS domain: home.arpa

I originally wanted names like:

  • bitwarden.rory
  • pve.rory

But using fake TLDs can cause name collisions and odd behavior. Instead, the correct standard domain for home networks is:

home.arpa

So the final naming scheme became:

  • bitwarden.home.arpa
  • pihole.home.arpa
  • (future) pve.home.arpa, nas.home.arpa, etc.

5. Adding Pi-hole on the same Pi (Virgin Hub 4 limitation)

Virgin Media Hub 4 makes it easy to reserve an IP via DHCP, but it does not reliably support pushing a custom DNS server to LAN clients the way a “real” router does. Pi-hole solves this at the network level.

Before installing Pi-hole, I checked if port 53 was free:

sudo ss -ltnup | grep ':53' || true
sudo ss -lnup  | grep ':53' || true

Only Avahi on port 5353 was present, which is fine. DNS port 53 was available.

Pi IP state

The Pi showed:

inet 192.168.*.***/24 ... dynamic

Meaning it was still using DHCP. That’s fine initially, but before making Pi-hole the DHCP server later, a truly static IP is required.


6. Running Pi-hole with Docker (host networking)

Pi-hole works best with network_mode: host if DHCP will be enabled later. It also avoids Docker bridge limitations for DHCP.

Update docker-compose.yml to include Pi-hole:

services:
  bitwarden:
    image: ghcr.io/bitwarden/lite
    restart: always
    env_file:
      - settings.env
    volumes:
      - bwdata:/etc/bitwarden
    ports:
      - "8080:8080"

  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    restart: unless-stopped
    network_mode: host
    cap_add:
      - NET_ADMIN
    environment:
      TZ: "Europe/London"
      FTLCONF_webserver_api_password: "CHANGE_THIS_PASSWORD"
      FTLCONF_dns_listeningMode: "ALL"
      FTLCONF_webserver_port: "8081o,[::]:8081o"
    volumes:
      - ./etc-pihole:/etc/pihole

volumes:
  bwdata:

Create the Pi-hole data directory:

mkdir -p ./etc-pihole

Restart the stack:

docker compose down
docker compose up -d
docker ps

Pi-hole UI is now available at:

  • http://192.168.*.***:8081/admin

7. Pi-hole admin password (and how it behaves)

Pi-hole asked for an admin password, but it wasn’t printed in the terminal because the stack was started detached.

Trying to set it interactively with:

docker exec -it pihole pihole setpassword

returned:

webserver.api.password set by environment variable. Please unset it to use this function

Lesson learned:

If you set FTLCONF_webserver_api_password in Compose, Pi-hole will require that password and block interactive password changes.

Shell history warning

If you ever set a password as a CLI argument, it will be written to shell history. To remove it:

history
history -d <LINE_NUMBER>
history -w

8. Creating LAN DNS records inside Pi-hole

In the Pi-hole UI:

Local DNS → DNS Records

Add:

  • pihole.home.arpa192.168.*.***
  • bitwarden.home.arpa192.168.*.***

This may feel odd because both names point to the same IP. That is normal. DNS just returns an IP address; the port or proxy decides which service you get.

At this stage:

  • Pi-hole: http://pihole.home.arpa:8081/admin
  • Bitwarden: http://bitwarden.home.arpa:8080

9. Fedora testing: why DNS broke with Proton VPN

My Fedora workstation was used to test Pi-hole resolution. With Proton VPN enabled, DNS failed even though Pi-hole was correct.

resolvectl status showed Proton’s interface (proton0) had:

  • DNS domain: ~. (catch-all)
  • DNS server: 10.2.0.1

Meaning:

all DNS queries were going through Proton VPN DNS, not my LAN Pi-hole.

To confirm LAN routing still worked, I ran:

ip route get 192.168.*.***

It correctly routed via my LAN interface (not via the VPN), so the issue was DNS only.

Fix: split DNS only for home.arpa

This preserves VPN DNS for internet domains, but forces home.arpa queries to Pi-hole:

sudo resolvectl dns enp0s13f0u3u2c2 192.168.*.***
sudo resolvectl domain enp0s13f0u3u2c2 '~home.arpa'
sudo resolvectl flush-caches

After that:

resolvectl query pihole.home.arpa
resolvectl query bitwarden.home.arpa

Both returned 192.168.*.*** even with the VPN enabled.


10. Bitwarden registration error: “Unhandled server error”

Even when the Bitwarden web UI loaded, creating an account initially failed with a red popup:

  • “Unhandled server error occurred”

This happened for two reasons during troubleshooting:

Bad Installation ID / Key causes backend crashes

Using placeholders or malformed values led to SIGABRT crashes in the container logs, and registration could never work:

docker logs -f bitwarden-lite-bitwarden-1

Correct fix: request valid ID+Key from Bitwarden and paste them into settings.env.

Startup warm-up delay

Even once services showed as “RUNNING”, registration sometimes failed briefly while the stack finished initialization. After a short wait, it started working without any config changes.


11. Final working state

The final LAN-only services:

  • Pi-hole web UI:

  • https://pihole.home.arpa/admin

  • Bitwarden Lite:

  • https://bitwarden.home.arpa

And DNS-based service discovery works across the LAN via Pi-hole local DNS.


Summary

Key lessons from this build:

  • Use Docker Compose v2 (docker compose) instead of old docker-compose binaries.
  • Don’t clone bitwarden/server; use Bitwarden Lite or the official installer.
  • Use home.arpa for internal naming.
  • Pi-hole local DNS makes homelab hostnames clean and scalable.
  • VPNs often “own” DNS; split DNS fixes local resolution without breaking VPN security.
  • Bitwarden Lite needs real Installation ID/Key and sometimes a short warm-up before registration works.