DNS Filtering
    Networking

    Deploy Blocky on a VPS

    Network-wide DNS ad blocking with encrypted upstream resolvers, per-client group policies, caching with prefetching, and Prometheus metrics — all from a single Go binary.

    At a Glance

    ProjectBlocky by 0xERR0R
    LicenseApache 2.0
    Recommended PlanRamNode 512 MB (1 GB recommended)
    OSUbuntu 24.04 LTS
    DNS Port53 (TCP/UDP)
    API/Metrics Port4000
    Estimated Setup Time15–20 minutes

    Prerequisites

    • A RamNode VPS with at least 512 MB RAM (1 GB recommended)
    • Ubuntu 24.04 LTS
    • Root or sudo access
    • SSH access to your VPS
    • A domain name (optional, required for DoH endpoint)
    1

    Prepare the VPS

    Update and install utilities
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y curl wget tar jq

    Disable systemd-resolved

    Ubuntu ships with systemd-resolved on port 53. Blocky needs this port.

    Disable systemd-resolved
    sudo systemctl stop systemd-resolved
    sudo systemctl disable systemd-resolved
    Replace resolv.conf
    sudo rm /etc/resolv.conf
    echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
    Verify port 53 is free
    sudo ss -tulnp | grep :53
    2

    Download and Install Blocky

    Download and install
    BLOCKY_VERSION="v0.29.0"
    wget https://github.com/0xERR0R/blocky/releases/download/${BLOCKY_VERSION}/blocky_${BLOCKY_VERSION}_Linux_x86_64.tar.gz -O /tmp/blocky.tar.gz
    tar -xzf /tmp/blocky.tar.gz -C /tmp/
    sudo mv /tmp/blocky /usr/local/bin/blocky
    sudo chmod +x /usr/local/bin/blocky

    Grant the binary permission to bind to privileged ports:

    Set capabilities
    sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/blocky
    Verify
    blocky version
    3

    Create a Dedicated Service User

    Create user and config directory
    sudo useradd -r -s /usr/sbin/nologin -M blocky
    sudo mkdir -p /etc/blocky
    sudo chown blocky:blocky /etc/blocky
    4

    Configure Blocky

    This configuration sets up encrypted upstream resolvers, ad/tracker blocking, DNS caching with prefetching, and Prometheus metrics.

    /etc/blocky/config.yml
    upstreams:
      init:
        strategy: fast
      groups:
        default:
          - https://cloudflare-dns.com/dns-query
          - https://dns.google/dns-query
          - tcp-tls:dns.quad9.net:853
          - tcp-tls:one.one.one.one:853
      strategy: parallel_best
      timeout: 5s
    
    bootstrapDns:
      - upstream: tcp+udp:1.1.1.1
      - upstream: tcp+udp:8.8.8.8
    
    blocking:
      denylists:
        ads:
          - https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
          - https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt
          - https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt
          - https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext
        malware:
          - https://urlhaus.abuse.ch/downloads/hostfile/
      allowlists:
        ads:
          - |
            # Add false-positive domains here
      clientGroupsBlock:
        default:
          - ads
          - malware
      blockType: zeroIp
      blockTTL: 1m
      loading:
        refreshPeriod: 4h
        downloads:
          timeout: 60s
          attempts: 3
          cooldown: 10s
        concurrency: 4
        strategy: blocking
        maxErrorsPerSource: 5
    
    caching:
      minTime: 5m
      maxTime: 30m
      prefetching: true
      prefetchExpires: 2h
      prefetchThreshold: 5
      cacheTimeNegative: 30m
    
    ports:
      dns: 53
      http: 4000
    
    prometheus:
      enable: true
      path: /metrics
    
    queryLog:
      type: console
    
    log:
      level: info
      format: text
      timestamp: true
    
    fqdnOnly:
      enable: false
    Set permissions
    sudo chown blocky:blocky /etc/blocky/config.yml
    sudo chmod 640 /etc/blocky/config.yml
    5

    Create a systemd Service

    /etc/systemd/system/blocky.service
    [Unit]
    Description=Blocky DNS Proxy and Ad Blocker
    Documentation=https://0xerr0r.github.io/blocky/
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=simple
    User=blocky
    Group=blocky
    ExecStart=/usr/local/bin/blocky --config /etc/blocky/config.yml
    Restart=on-failure
    RestartSec=10
    
    NoNewPrivileges=true
    ProtectSystem=strict
    ProtectHome=true
    ReadOnlyPaths=/etc/blocky
    PrivateTmp=true
    ProtectKernelTunables=true
    ProtectKernelModules=true
    ProtectControlGroups=true
    MemoryDenyWriteExecute=true
    LockPersonality=true
    
    AmbientCapabilities=CAP_NET_BIND_SERVICE
    CapabilityBoundingSet=CAP_NET_BIND_SERVICE
    
    [Install]
    WantedBy=multi-user.target
    Enable and start
    sudo systemctl daemon-reload
    sudo systemctl enable blocky
    sudo systemctl start blocky
    Check status
    sudo systemctl status blocky
    6

    Test DNS Resolution

    Test legitimate domain
    dig @127.0.0.1 google.com
    Test ad blocking
    dig @127.0.0.1 ads.google.com

    The ad domain should return 0.0.0.0 (the configured blockType: zeroIp response).

    CLI tools
    blocky blocking status
    blocky query google.com
    blocky query ads.google.com
    7

    Configure the Firewall

    UFW rules
    sudo apt install -y ufw
    sudo ufw allow 22/tcp
    sudo ufw allow 53/tcp
    sudo ufw allow 53/udp
    sudo ufw --force enable
    sudo ufw status verbose

    Important: If exposing Blocky publicly, restrict port 53 to known client IPs to prevent DNS amplification attacks:

    Restrict to specific IPs
    sudo ufw delete allow 53/tcp
    sudo ufw delete allow 53/udp
    sudo ufw allow from YOUR_HOME_IP to any port 53 proto tcp
    sudo ufw allow from YOUR_HOME_IP to any port 53 proto udp
    8

    Point Your Devices to Blocky

    Router: Change DNS settings to your VPS public IP for network-wide filtering.

    Linux:

    Set DNS
    echo "nameserver YOUR_VPS_IP" | sudo tee /etc/resolv.conf

    macOS: System Preferences → Network → Advanced → DNS. Windows: Network & Internet Settings → Adapter → IPv4 → Preferred DNS.

    9

    Per-Client Group Filtering (Optional)

    config.yml snippet
    blocking:
      denylists:
        ads:
          - https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
        malware:
          - https://urlhaus.abuse.ch/downloads/hostfile/
        adult:
          - https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/porn/hosts
      clientGroupsBlock:
        default:
          - ads
          - malware
        192.168.1.100/32:
          - ads
          - malware
          - adult
        kids-*:
          - ads
          - malware
          - adult
    Restart after changes
    sudo systemctl restart blocky
    10

    Monitor with Prometheus and Grafana (Optional)

    Blocky exposes Prometheus metrics at http://YOUR_VPS_IP:4000/metrics.

    Prometheus scrape config
    scrape_configs:
      - job_name: 'blocky'
        static_configs:
          - targets: ['YOUR_VPS_IP:4000']
    Verify metrics endpoint
    curl http://localhost:4000/metrics

    Maintenance

    Updating Blocky

    Update to new release
    sudo systemctl stop blocky
    BLOCKY_VERSION="v0.30.0"
    wget https://github.com/0xERR0R/blocky/releases/download/${BLOCKY_VERSION}/blocky_${BLOCKY_VERSION}_Linux_x86_64.tar.gz -O /tmp/blocky.tar.gz
    tar -xzf /tmp/blocky.tar.gz -C /tmp/
    sudo mv /tmp/blocky /usr/local/bin/blocky
    sudo chmod +x /usr/local/bin/blocky
    sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/blocky
    sudo systemctl start blocky
    blocky version

    Runtime Controls

    Manage blocking at runtime
    blocky blocking disable --duration 5m
    blocky blocking disable --groups ads
    blocky blocking enable
    blocky blocking status
    blocky lists refresh

    Troubleshooting

    "Address already in use"

    Another process is using port 53. Run sudo ss -tulnp | grep :53 to identify it. Common culprits: systemd-resolved, dnsmasq.

    Deny lists fail to download

    Check outbound HTTPS: curl -I https://raw.githubusercontent.com. Increase loading.downloads.timeout if timing out.

    DNS queries are slow

    Test upstream directly: dig @1.1.1.1 google.com. Increase caching.maxTime and lower caching.prefetchThreshold.

    Blocky not blocking ads

    Run blocky query ads.google.com and check logs: sudo journalctl -u blocky -f. Verify clientGroupsBlock.default includes correct list names.