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
| Project | Blocky by 0xERR0R |
| License | Apache 2.0 |
| Recommended Plan | RamNode 512 MB (1 GB recommended) |
| OS | Ubuntu 24.04 LTS |
| DNS Port | 53 (TCP/UDP) |
| API/Metrics Port | 4000 |
| Estimated Setup Time | 15–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)
Prepare the VPS
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget tar jqDisable systemd-resolved
Ubuntu ships with systemd-resolved on port 53. Blocky needs this port.
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolvedsudo rm /etc/resolv.conf
echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.confsudo ss -tulnp | grep :53Download and Install Blocky
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/blockyGrant the binary permission to bind to privileged ports:
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/blockyblocky versionCreate a Dedicated Service User
sudo useradd -r -s /usr/sbin/nologin -M blocky
sudo mkdir -p /etc/blocky
sudo chown blocky:blocky /etc/blockyConfigure Blocky
This configuration sets up encrypted upstream resolvers, ad/tracker blocking, DNS caching with prefetching, and Prometheus metrics.
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: falsesudo chown blocky:blocky /etc/blocky/config.yml
sudo chmod 640 /etc/blocky/config.ymlCreate a systemd 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.targetsudo systemctl daemon-reload
sudo systemctl enable blocky
sudo systemctl start blockysudo systemctl status blockyTest DNS Resolution
dig @127.0.0.1 google.comdig @127.0.0.1 ads.google.comThe ad domain should return 0.0.0.0 (the configured blockType: zeroIp response).
blocky blocking status
blocky query google.com
blocky query ads.google.comConfigure the Firewall
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 verboseImportant: If exposing Blocky publicly, restrict port 53 to known client IPs to prevent DNS amplification attacks:
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 udpPoint Your Devices to Blocky
Router: Change DNS settings to your VPS public IP for network-wide filtering.
Linux:
echo "nameserver YOUR_VPS_IP" | sudo tee /etc/resolv.confmacOS: System Preferences → Network → Advanced → DNS. Windows: Network & Internet Settings → Adapter → IPv4 → Preferred DNS.
Per-Client Group Filtering (Optional)
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
- adultsudo systemctl restart blockyMonitor with Prometheus and Grafana (Optional)
Blocky exposes Prometheus metrics at http://YOUR_VPS_IP:4000/metrics.
scrape_configs:
- job_name: 'blocky'
static_configs:
- targets: ['YOUR_VPS_IP:4000']curl http://localhost:4000/metricsMaintenance
Updating Blocky
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 versionRuntime Controls
blocky blocking disable --duration 5m
blocky blocking disable --groups ads
blocky blocking enable
blocky blocking status
blocky lists refreshTroubleshooting
"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.
