Skip to content

Plan: Migrate dns-ad-blocker to Blocky

Status

State: Active Started: 2026-05-14

Context

The current dns-ad-blocker service uses oznu/dns-ad-blocker:latest. This image is critically outdated:

  • Last image build: ~2020 (5+ years old)
  • Repository status: Archived by owner on April 30, 2022 (read-only)
  • Security risk: Running unmaintained base OS, outdated dnsmasq/dnscrypt-proxy binaries, no upstream CVE patches
  • Tag risk: Uses :latest rolling tag with no actual updates

The service provides network-wide DNS resolution with ad blocking for the 192.168.2.0/24 LAN via port 53 on 192.168.2.151.

Sepia uses dnsmasq on the host for DHCP (not handled by the container). The dns-ad-blocker container handles DNS resolution and ad blocking only.

Current Architecture

LAN clients → dnsmasq (host, port 53, DHCP)
                   │
                   ▼ /etc/resolv.conf → dns-ad-blocker or upstream

Proposed: Blocky

Image: ghcr.io/0xerr0r/blocky:v0.25 (or latest stable) Repository: https://github.com/0xERR0R/blocky Why: Stateless (config-as-code), single Go binary (lightweight), native DoH/DoT, built-in Prometheus metrics, no mandatory web UI, actively maintained.

Blocky fits sepia's infrastructure philosophy — config-as-code, lightweight, metrics-native.

Goals

  • [ ] Create Blocky config with equivalent ad-blocking, whitelist, and upstream DNS
  • [ ] Create Blocky compose file
  • [ ] Test Blocky alongside existing dns-ad-blocker
  • [ ] Cut over: stop dns-ad-blocker, start Blocky
  • [ ] Update docs (services.md, network.md, caddy.md)
  • [ ] Clean up old dns-ad-blocker files after stability period

Steps

Step 1: Create Blocky Config

Create /opt/blocky/config.yml:

upstream:
  default:
    - 1.1.1.1
    - 1.0.0.1

blocking:
  blackLists:
    ads:
      - https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
      - https://someonewhocares.org/hosts/zero/hosts
  whiteLists:
    ads:
      - api.segment.io
      - www.googleapis.com
      - analytics.google.com
  clientGroupsBlock:
    default:
      - ads
  blockType: zeroIp

caching:
  minTime: 5m
  maxTime: 30m
  prefetching: true

prometheus:
  enable: true
  path: /metrics

ports:
  dns: 53
  http: 4000

log:
  level: info

Whitelist notes: Current whitelist from dns-ad-blocker includes api.segment.io, www.googleapis.com, analytics.google.com. Custom rules from custom.conf need review — these may include Google ad service bypass rules that should also be in the whitelist.

  • Verification: touch /opt/blocky/config.yml && docker run --rm -v /opt/blocky/config.yml:/app/config.yml:ro ghcr.io/0xerr0r/blocky:v0.25 --validate-config passes

Step 2: Create Blocky Compose File

Create /opt/compose.blocky.yaml:

services:
  blocky:
    image: ghcr.io/0xerr0r/blocky:v0.25
    container_name: blocky
    ports:
      - 192.168.2.151:53:53/tcp
      - 192.168.2.151:53:53/udp
      - 4000:4000/tcp
    environment:
      - TZ=Europe/Amsterdam
    volumes:
      - /opt/blocky/config.yml:/app/config.yml:ro
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:4000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

Bind to 192.168.2.151 on port 53 (same IP as the current dns-ad-blocker gets via network alias).

  • Verification: docker compose -f compose.blocky.yaml config validates

Step 3: Test Blocky Alongside

Deploy Blocky while dns-ad-blocker is still running:

# Stop dns-ad-blocker briefly to free port 53
docker compose -f compose.dns-ad-blocker.yaml down

# Start Blocky
docker compose -f compose.blocky.yaml up -d

# Test resolution
dig @192.168.2.151 google.com
dig @192.168.2.151 doubleclick.net  # should return 0.0.0.0

Test cases: - Regular DNS resolution works (google.com, github.com) - Ad domains return 0.0.0.0 (doubleclick.net, adservice.google.com) - Whitelisted domains resolve normally (api.segment.io) - LAN hostnames resolve (veenboer.xyz local services)

  • Verification: All test cases pass

Step 4: Update Compose Includes

Edit /opt/compose.yaml: - Remove compose.dns-ad-blocker.yaml from includes - Add compose.blocky.yaml

docker compose up -d blocky
docker compose ps  # verify blocky is healthy
  • Verification: dig @192.168.2.151 google.com resolves from LAN clients

Step 5: Cleanup (After 2-Week Stability)

  • [ ] Remove /opt/compose.dns-ad-blocker.yaml
  • [ ] Remove /opt/dns-ad-blocker/ directory
  • [ ] Remove old dnsmasq config files in that directory
  • [ ] Update REFERENCE/services.md — replace entry
  • [ ] Update REFERENCE/network.md — update port 53 mapping
  • [ ] Update REFERENCE/caddy.md — if any caddy routes reference it
  • [ ] Add cleanup reminder to HEARTBEAT.md (2 weeks)

Rollback

If issues arise within 2 weeks:

# Stop Blocky
docker compose -f compose.blocky.yaml down

# Restore old dns-ad-blocker
docker compose -f compose.dns-ad-blocker.yaml up -d

# Revert compose.yaml includes
git checkout compose.yaml
  • PLANS/active/infrastructure-hardening.md (healthcheck for dns-ad-blocker is deferred to this plan)
  • REFERENCE/services.md
  • REFERENCE/network.md
  • REFERENCE/caddy.md

Created: 2026-05-14