Block ads on all your devices: 4 solutions for a Linux VPS

Block ads on all your devices: 4 solutions for a Linux VPS

August 18, 2026

Block ads on all your devices: 4 solutions for a Linux VPS

Online ads can quickly become exhausting: banners that take over the page, autoplay videos, advertising trackers and promotional content appearing not only in browsers but also in apps and smart TVs. For years, the obvious answer was to install an ad-blocking Chrome extension.

That approach has limits. A browser extension mainly protects the browser where it is installed, not necessarily your apps, phones, TVs or other devices. Browsers are also tightening the rules around extensions. In Chrome, for example, Manifest V3, Chrome’s current extension framework, removed the old webRequestBlocking permission for most extensions, forcing content blockers to adapt how they filter network requests.

That does not mean ad blockers are banned. They still exist, but depending on the browser and device they may be less capable, unavailable or simply impossible to install. Another approach is to block advertising and tracking domains at the network level, before they reach your browser or applications.

This approach uses a filtering DNS server. DNS is the directory your devices use to turn a domain name into the address of a server. A filtering DNS service can refuse to resolve domains known for advertising or tracking, stopping the connection before the content even loads. Hosted on a Linux VPS, it can protect several devices and networks at once while keeping blocklists, logs and rules under your control. In this guide, we compare four open-source projects: AdGuard Home, Technitium DNS Server, Blocky and Pi-hole.

They share the same broad goal but take very different approaches. We will first compare the essentials, then look at the advantages and drawbacks of each one on a public VPS.

Quick comparison

Three acronyms appear often in this article. DoH (DNS-over-HTTPS) carries DNS queries inside a normal HTTPS connection, usually on port 443. DoT (DNS-over-TLS) encrypts DNS inside a dedicated TLS connection, usually on port 853. DoQ (DNS-over-QUIC) uses QUIC, a modern transport protocol built on UDP. TLS is the encryption mechanism used by HTTPS among other protocols. In all three cases, the goal is to stop intermediaries from easily reading DNS queries between your device and the server.

Solution GitHub popularity* Built-in Web UI Encrypted DNS server Best for
AdGuard Home ~36,000 stars Yes Native DoH, DoT and DoQ Recommended: simple all-in-one VPS choice
Technitium DNS Server ~9,500 stars Yes Native DoH, DoT and DoQ Advanced users, full DNS administration
Blocky ~6,900 stars No, third-party UIs available DoH/DoT, DoQ for upstream resolvers DevOps, YAML, low footprint
Pi-hole ~60,000 stars Yes DoH/DoT with an extra component Local networks or experts, less suited to a VPS

* GitHub stars checked in August 2026. They are a useful indicator of open-source traction, but they are not an installation count.

Our recommendation for most users: AdGuard Home. It combines filtering, a clear interface and encrypted DNS protocols in one application. If you simply want to block ads on PCs and phones from a VPS, it is the most direct choice.

Pi-hole is deliberately last for this use case. It remains excellent on a local network, but on a VPS you need extra components for clean encrypted DNS. We therefore do not recommend it to VPS beginners.

Before choosing, there is one major difference between running filtering DNS at home and running it on the public Internet.

On a VPS, never create an open DNS resolver

On a home network, the DNS server is usually invisible from the Internet. On a VPS, its IPv4 address is public: this is the Internet address used to reach the server directly. Simply opening DNS port 53 to everyone can turn your server into an open resolver, meaning anyone can use it.

That is not only a privacy problem. Open resolvers can be abused in DNS amplification attacks, where small requests trigger much larger responses toward a victim, and generate traffic you never requested.

For personal DNS hosted on a VPS, three models are possible:

  • classic DNS on the public IP, with port 53 restricted by the firewall to authorized source IP addresses only.
  • Access through a private network, such as WireGuard, with port 53 reachable only from that network.
  • encrypted DNS through DoH or DoT, with TLS, client restrictions and appropriate limits.

Encryption does not replace access control. A public DoH or DoT service with no restrictions can still be misused by third parties.

The administration interface deserves the same care. Do not leave a dashboard exposed on a Web port without protection. Restrict it to the VPN or trusted IP addresses, or place it behind a properly secured reverse proxy, meaning a Web server placed in front of the interface to handle HTTPS, authentication and access rules.

AdGuard Home: our simple recommendation for a VPS

AdGuard Home uses the same broad idea of network-level DNS filtering, but its feature set is especially convenient on a VPS. One service provides filtering, a Web interface, per-client rules and the main modern encrypted DNS protocols.

It can directly serve DNS-over-HTTPS (DoH), DNS-over-TLS (DoT) and DNS-over-QUIC (DoQ). An official Docker image is available, and configuration data can be kept in persistent volumes.

The interface brings DNS queries, blocking statistics, top clients and filtering controls together in one place. That is one reason AdGuard Home is especially approachable on a VPS.

AdGuard Home advantages

  • Built-in DoH, DoT and DoQ without an additional service.
  • Modern and approachable Web interface.
  • Custom blocklists, rules and DNS rewrites.
  • Different policies for different clients.
  • Native Linux installation or official Docker image.
  • No usage-statistics collection by default.
  • Fully open-source project.

AdGuard Home disadvantages

  • heavier than a minimal DNS proxy such as Blocky, mainly because of the interface and the query logs.
  • less suitable than a full DNS server such as Technitium if you want to manage zones, advanced recursion or clustering.
  • DoH, DoT and DoQ require a domain name and a TLS certificate: this is not a simple toggle.
  • the Docker setup wizard is a two-step process, first port 3000 then port 80, so the first launch needs a bit of care.

Our recommendation: if you want to install one application, get a simple interface and easily use your DNS from Windows, Android, iPhone or iPad, choose AdGuard Home.

AdGuard Home installation tutorial with Docker

Install AdGuard Home on your Linux VPS

This installation uses the official AdGuard Home Docker image. If Docker is not installed on your VPS yet, start with our Install Docker on Linux guide.

We will begin with a deliberately private configuration: DNS, the setup wizard and the administration panel will only be reachable from the VPS. This lets you finish the configuration without exposing a DNS resolver to the Internet.

Create the persistent AdGuard Home directories:

mkdir -p /opt/adguardhome/work /opt/adguardhome/conf
cd /opt/adguardhome

Then create compose.yaml:

cat > compose.yaml <<'EOF'
services:
  adguardhome:
    container_name: adguardhome
    image: adguard/adguardhome:latest
    ports:
      - "127.0.0.1:53:53/tcp"
      - "127.0.0.1:53:53/udp"
      - "127.0.0.1:3000:3000/tcp"
      - "127.0.0.1:8081:80/tcp"
    volumes:
      - "./work:/opt/adguardhome/work"
      - "./conf:/opt/adguardhome/conf"
    restart: unless-stopped
EOF

Start the container:

docker compose up -d
docker compose ps

On the first launch, AdGuard Home opens its setup wizard on port 3000. Because that port is bound to 127.0.0.1, open an SSH tunnel from your computer:

ssh -L 3000:127.0.0.1:3000 root@VPS_IP

Keep this SSH connection open, then browse to http://127.0.0.1:3000/.

Complete the setup wizard

The wizard first asks which interfaces and ports to use. With the Compose file above, you can keep:

  • Admin Web interface: all interfaces, port 80.
  • DNS server: all interfaces, port 53.

Those choices apply inside the container. On the VPS itself, Docker still publishes DNS only on 127.0.0.1:53 and the Web interface only on 127.0.0.1:8081.

You can keep the proposed upstream DNS servers to get started. If you prefer to reuse the resolvers already used by the VPS, enter their real IP addresses later under Settings > DNS settings > Upstream DNS servers. Do not use 127.0.0.53 from inside the container.

Create your administrator username and a strong password, then finish the wizard.

After setup, the main panel listens on port 80 inside the container. Open a new tunnel:

ssh -L 8081:127.0.0.1:8081 root@VPS_IP

You can now open http://127.0.0.1:8081/ and sign in with the account you created in the wizard.

Check that filtering works

If dig is not installed on your VPS yet, install it and query AdGuard Home directly:

apt update
apt install -y dnsutils
dig @127.0.0.1 pi-hole.net +short
dig @127.0.0.1 doubleclick.net +short

The first query should return a normal IP address. With the default filters currently enabled, doubleclick.net is blocked and returns 0.0.0.0.

You can confirm the result in Query Log. The dashboard also shows total queries, blocked domains and the most active clients.

Let your devices use AdGuard Home

The Compose file deliberately keeps DNS on 127.0.0.1 during installation. Once AdGuard Home is working, choose the access method that fits your devices:

  • Classic DNS: publish port 53 on the VPS IPv4 address only after restricting the firewall to authorized source IP addresses.
  • DoH, DoT or DoQ: configure encryption with a domain name and TLS certificate. This is usually the most convenient choice for phones and devices that often change networks.
  • Private network: keep port 53 on a private address if you use WireGuard or another VPN.

Port 3000 is no longer needed after the setup wizard, so you can remove that mapping from the Compose file.

What if you prefer DoH, DoT or DoQ?

AdGuard Home can also serve encrypted DNS natively. In practice, DoH uses HTTPS, DoT uses a dedicated TLS connection and DoQ uses QUIC. This is useful when you want to use your DNS outside your VPN, but it requires a domain name pointing to the VPS and a valid TLS certificate.

In Settings > Encryption settings, enable encryption and provide the domain name, certificate and private key. Only expose the corresponding ports once this configuration is complete.

AdGuard Home commonly uses port 443 for DoH and port 853 for DoT/DoQ. If you choose this mode, publish only the protocols you actually need and keep an appropriate access policy. A publicly reachable encrypted DNS service is still an Internet service that needs to be administered accordingly.

If you do not want to expose any DNS service publicly, private VPN + private port 53 remains an excellent option. If you prefer not to install a VPN on your devices, DoH or DoT lets you use AdGuard Home directly with encrypted DNS.

If port 53 is already in use

On a standard Ubuntu installation, this 127.0.0.1 binding can coexist with systemd-resolved. If Docker still reports that the port is already allocated, identify what is listening before changing the system:

ss -lntup | grep ':53'

Do not disable a system resolver blindly. Check the exact address it is bound to first and adjust your setup only if necessary.

Update AdGuard Home

Persistent data is stored in /opt/adguardhome/work and /opt/adguardhome/conf. To pull a newer version of the official image:

cd /opt/adguardhome
docker compose pull
docker compose up -d

The compose.yaml file and the two persistent directories are enough to preserve the installation when the container is recreated. Back up /opt/adguardhome before major changes.

Technitium DNS Server: powerful, but more advanced

Technitium DNS Server sits in a slightly different category. While AdGuard Home and Pi-hole put filtering first, Technitium is primarily a full DNS server. It can be recursive, meaning it looks up answers from other DNS servers on behalf of a client, or authoritative, meaning it answers for DNS zones for which it is the official source.

It then adds domain blocking, ad and malware blocklists, advanced caching, logs, an API, clustering for coordinating several instances, and a very complete Web console. It can also directly host DoH, DoT and DoQ services.

Its console reflects the more advanced positioning: detailed statistics, DNS zones, cache, rules, apps and administration are all available from one interface.

Technitium DNS Server advantages

  • Full recursive and authoritative DNS server.
  • Built-in DoH, DoT and DoQ.
  • Blocklists and advanced DNS rules.
  • Very feature-rich Web interface.
  • HTTP API and advanced administration features.
  • Recursion access control through ACLs, lists that define which IP addresses or networks are allowed.
  • Official Linux installation and Docker image.
  • Clustering for architectures that need multiple instances.

Technitium DNS Server disadvantages

Its power is also its main drawback if all you want is simple ad blocking. There are more DNS concepts to understand and more settings to manage than with Pi-hole or AdGuard Home.

On a public VPS, you should pay particular attention to the recursion policy, meaning who is allowed to ask Technitium to resolve DNS names on their behalf. ACLs (Access Control Lists) restrict that permission to selected IP addresses or networks. An overly permissive configuration can expose a recursive resolver to the Internet when you only wanted to serve a few personal devices.

Choose it if: you want to learn or manage DNS in more depth, host zones, perform recursion yourself, or use a platform that goes far beyond basic filtering.

Technitium DNS Server installation tutorial with Docker

Install Technitium DNS Server on your Linux VPS

This installation uses the official Technitium DNS Server Docker image. If Docker is not installed on your VPS yet, start with our Install Docker on Linux guide.

We will initially bind both DNS and the Web console to 127.0.0.1. This lets you finish the setup and verify filtering without publishing a DNS resolver on the Internet.

Create the Technitium folders and a random administrator password:

mkdir -p /opt/technitium-dns/config /opt/technitium-dns/logs
cd /opt/technitium-dns
printf 'TECHNITIUM_PASSWORD=%s\n' "$(openssl rand -hex 18)" > .env
chmod 600 .env

Then create compose.yaml:

cat > compose.yaml <<'EOF'
services:
  dns-server:
    container_name: technitium-dns
    hostname: dns-server
    image: technitium/dns-server:latest
    ports:
      - "127.0.0.1:5380:5380/tcp"
      - "127.0.0.1:53:53/udp"
      - "127.0.0.1:53:53/tcp"
    environment:
      DNS_SERVER_DOMAIN: "dns.example"
      DNS_SERVER_ADMIN_PASSWORD: "${TECHNITIUM_PASSWORD}"
      DNS_SERVER_LOG_FOLDER_PATH: "/var/log/technitium/dns"
    volumes:
      - "./config:/etc/dns"
      - "./logs:/var/log/technitium/dns"
    restart: unless-stopped
    sysctls:
      net.ipv4.ip_local_port_range: "1024 65535"
EOF

dns.example is only used here as the server identifier. If you later configure DoH, DoT or DoQ with a real DNS name, replace it with the actual domain name you use.

Start the container:

docker compose up -d
docker compose ps

The Technitium Web console listens on port 5380. DNS port 53 is still reachable only from the VPS at this stage.

Connect to the Web console

From your computer, open an SSH tunnel:

ssh -L 5380:127.0.0.1:5380 root@VPS_IP

Keep that SSH connection open, then browse to http://127.0.0.1:5380/. The initial username is admin.

To display the password generated during the first startup:

cd /opt/technitium-dns
grep '^TECHNITIUM_PASSWORD=' .env | cut -d= -f2-

Log in with that password. DNS_SERVER_ADMIN_PASSWORD is only used to initialize the account when no Technitium configuration exists yet. If you later change the password from the Web console, the .env file will no longer be its source of truth.

Enable an ad-blocking list

In the Technitium console, open Settings > Blocking. Blocking itself is enabled by default, but a fresh installation does not yet contain a large external blocklist.

Under Quick Add, choose Default, then click Save Settings. Technitium adds its default blocklist and downloads it automatically. You can add more lists later if you actually need them, but stacking millions of domains for no reason only increases memory usage.

The recommended default blocking type is NX Domain. This means a blocked domain does not necessarily return 0.0.0.0: the server instead answers that the domain does not exist.

Verify DNS filtering

If dig is not available yet, install dnsutils first:

apt update
apt install -y dnsutils

Then query a normal domain and an advertising domain:

dig @127.0.0.1 pi-hole.net +short
dig @127.0.0.1 doubleclick.net +noall +answer +comments

The first command should return a normal IP address. For a blocked domain, look for status: NXDOMAIN and EDE: 15 (Blocked) in the second response. EDE means Extended DNS Error: here, this code indicates that a DNS rule blocked the response. If the lists change and this example is no longer blocked, test another advertising domain present in your list.

The dashboard then lets you monitor queries, recursive answers, cache activity, blocked domains and clients.

Let your devices use Technitium

The Compose file deliberately keeps DNS on 127.0.0.1 during installation. After validation, choose one of the three access methods described later in this guide: firewall-restricted classic DNS, DoH/DoT/DoQ, or a private network.

If you make Technitium reachable through classic DNS, also check the recursion ACLs under Settings > Recursion so that only authorized addresses or networks can use it. For phones and mobile devices, DoH or DoT is usually more convenient than a firewall rule based on an IP address that may change.

What about DoH, DoT or DoQ?

Technitium can natively provide all three protocols. DoH carries DNS through HTTPS, DoT uses a dedicated TLS connection and DoQ uses QUIC. Enable them under Settings > Optional Protocols with a real domain name and a valid TLS certificate.

In Docker, the usual ports are 443/tcp and 443/udp for DoH, then 853/tcp and 853/udp for DoT and DoQ. Do not publish these ports until TLS and your access rules are configured.

If you want to expose nothing publicly, private VPN + private port 53 remains very easy to secure. Otherwise, Technitium’s native DoH or DoT support lets you use the server without a VPN.

If port 53 is already in use

This 127.0.0.1 binding can coexist with systemd-resolved when it only listens on its reserved local DNS addresses. If Docker still reports a conflict, first identify the exact address already in use:

ss -lntup | grep ':53'

Do not disable the system resolver until you have identified the actual conflict.

Update Technitium

Configuration is persisted in /opt/technitium-dns/config and logs in /opt/technitium-dns/logs. To update the official image:

cd /opt/technitium-dns
docker compose pull
docker compose up -d

Back up /opt/technitium-dns before major changes. The Web console also provides Backup Settings for exporting the DNS configuration.

Blocky: lightweight for DevOps users

Blocky takes almost the opposite approach. It is a DNS proxy and blocker written in Go, distributed as a single binary or Docker container, and mainly configured through YAML, a structured text format commonly used for readable configuration files.

It supports blocklists and allowlists, client groups, regular expressions, meaning text patterns that can match several domains with one rule, caching and prefetch, which refreshes likely-needed answers before they expire. It also supports DNSSEC, a cryptographic signature system used to verify the authenticity and integrity of DNS data, plus DoH and DoT. It can use upstream resolvers, meaning the DNS servers to which Blocky forwards queries. These can be the resolvers already used by your VPS or other resolvers of your choice, including through DoQ. For monitoring, Prometheus collects Blocky’s metrics and Grafana can turn them into charts and dashboards.

Blocky advantages

  • Very small footprint and simple deployment.
  • Single binary or multi-architecture Docker image.
  • Declarative configuration that is easy to version.
  • No mandatory database.
  • Filtering lists by client group and regular-expression rules.
  • Configurable cache and prefetch.
  • Prometheus metrics, REST API and Grafana integration.
  • No telemetry or hidden filtering.

Blocky disadvantages

Blocky does not include a large administration Web interface comparable to Pi-hole, AdGuard Home or Technitium. Community interfaces exist, and Grafana can provide excellent dashboards, but that means adding components if you want to manage everything graphically.

It also expects you to be more comfortable with YAML and server configuration. It can use DoQ for upstream resolvers, but it does not currently provide an incoming DoQ server as integrated as the one in AdGuard Home or Technitium.

Choose it if: you like simple services, version-controlled configuration files, Docker, Prometheus and automation, and a complete graphical interface is not your priority.

Blocky installation tutorial with Docker

Install Blocky on your Linux VPS

Blocky is mainly configured through a YAML file. This installation uses the official Docker image published by the project. If Docker is not installed yet, start with our Install Docker on Linux guide.

We will first publish DNS and the API only on 127.0.0.1. Once the installation is validated, you can choose firewall-restricted classic DNS, DoH/DoT, or a private network.

Create the Blocky directory:

mkdir -p /opt/blocky
cd /opt/blocky

Then create config.yml:

cat > config.yml <<'EOF'
upstreams:
  groups:
    default:
      - 1.1.1.1
      - 9.9.9.9
blocking:
  denylists:
    ads:
      - https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
  clientGroupsBlock:
    default:
      - ads
ports:
  dns: 53
  http: 4000
prometheus:
  enable: true
EOF

The two addresses under upstreams are example public resolvers. You can replace them with the resolvers you prefer, including the real addresses of the DNS servers already used by your VPS. The denylists section, where blocklists are declared, uses the StevenBlack list, which is also used in Blocky’s official documentation.

Now create compose.yaml:

cat > compose.yaml <<'EOF'
services:
  blocky:
    container_name: blocky
    image: ghcr.io/0xerr0r/blocky:latest
    ports:
      - "127.0.0.1:53:53/tcp"
      - "127.0.0.1:53:53/udp"
      - "127.0.0.1:4000:4000/tcp"
    environment:
      TZ: "America/New_York"
    volumes:
      - "./config.yml:/app/config.yml:ro"
    restart: unless-stopped
EOF

Start Blocky:

docker compose up -d
docker compose ps

The container should become healthy after the lists are loaded and the upstream resolvers are checked.

Validate the configuration and filtering

Blocky includes a configuration validation command. Use it to catch YAML errors before troubleshooting anything else:

docker exec blocky /app/blocky validate --config /app/config.yml

The output should confirm that the configuration is valid.

If dig is not installed yet, add dnsutils, then test a normal domain and an advertising domain:

apt update
apt install -y dnsutils
dig @127.0.0.1 pi-hole.net +short
dig @127.0.0.1 doubleclick.net +short

The first command should return a normal IP address. With the list used here, doubleclick.net is currently blocked and returns 0.0.0.0.

You can also check the blocking state through the local API:

curl http://127.0.0.1:4000/api/blocking/status

An active installation returns {"enabled":true}.

What is port 4000 for?

Blocky does not include a large administration dashboard like Pi-hole, AdGuard Home or Technitium. Port 4000 mainly serves its REST API and Prometheus metrics.

In this configuration it therefore remains deliberately bound to 127.0.0.1. For example, you can confirm that Prometheus data is available with:

curl http://127.0.0.1:4000/metrics | head

You do not need to open this port to the Internet to use Blocky as your DNS server.

Let your devices use Blocky

The Compose file deliberately keeps DNS on 127.0.0.1 during installation. Once Blocky is working, you can publish port 53 only to authorized IP addresses, configure its DoH/DoT server, or keep DNS on a private network.

Blocky is mainly aimed at users who are comfortable with configuration files. If your priority is a simple setup across several phones and PCs, AdGuard Home remains the better fit thanks to its interface and built-in encrypted protocols.

What about DoH or DoT?

Blocky can also serve DNS-over-HTTPS (DoH), where DNS queries travel through HTTPS, and DNS-over-TLS (DoT), where they use a dedicated TLS connection. On a public VPS, however, this needs more care than a private DNS port because Blocky’s HTTP/HTTPS services also expose its REST API, an interface intended for tools and scripts, along with metrics and diagnostic functions.

If you want to provide public DoH, keep Blocky behind a trusted reverse proxy and expose only the DNS path you actually need, usually /dns-query. If you prefer to expose nothing publicly, simply keep DNS on the private WireGuard IP.

Change the configuration and update Blocky

After editing config.yml, validate it and restart the container:

cd /opt/blocky
docker exec blocky /app/blocky validate --config /app/config.yml
docker compose restart

To update the official image:

cd /opt/blocky
docker compose pull
docker compose up -d

Your configuration remains in /opt/blocky/config.yml. Blocky also refreshes remote lists periodically, so you do not need to manually download the blocklist again for every update.

Pi-hole is by far the most popular project in our selection on GitHub and remains excellent on a local network. For DNS hosted on a VPS and used from several devices, however, it is not our default recommendation. Its idea is still straightforward: it receives DNS queries from your devices, compares requested domains against your filtering lists, and blocks the ones you do not want to allow.

Its Web interface gives quick access to queries, blocked domains, most active clients, lists, custom rules and statistics. It can be installed directly on Linux or deployed with the official Docker image.

The dashboard immediately highlights query volume, blocking rate and client activity, making it easy to see what the DNS service is actually filtering.

Pi-hole advantages

  • Very large community and documentation base.
  • Easy-to-understand Web interface.
  • Blocklists, allowlists and group-based rules.
  • Clear statistics and query logs.
  • Official Linux installation and Docker image.
  • Mature project, fully open source, with no required subscription.

Pi-hole disadvantages

Pi-hole was historically designed to run inside a trusted network. It handles classic DNS filtering very well, but does not natively serve DNS-over-HTTPS or DNS-over-TLS. To encrypt traffic between your devices and the VPS, you need an additional DoH/DoT proxy or a VPN.

That is not a problem on a local network. On a public VPS, however, it means adding and maintaining another component while AdGuard Home or Technitium already include these protocols. A solution that is very simple at home therefore becomes more complex on the Internet.

On a VPS, we only recommend Pi-hole to advanced users. Choose it in this context only if you are comfortable administering DNS, a VPN or a DoH/DoT proxy, or if you have a specific reason to want the Pi-hole ecosystem.

Pi-hole installation tutorial with Docker

Install Pi-hole on your Linux VPS

This installation uses the official Pi-hole Docker image. If Docker is not installed on your VPS yet, start with our Install Docker on Linux guide.

We will first bind Pi-hole only to 127.0.0.1. This lets you verify the installation without exposing a recursive DNS service to the Internet. After validation, you can choose firewall-restricted classic DNS, add a DoH/DoT component, or use a private network.

Create the Pi-hole directory and a random administration password:

mkdir -p /opt/pihole/etc-pihole
cd /opt/pihole
printf 'PIHOLE_PASSWORD=%s\n' "$(openssl rand -hex 18)" > .env
chmod 600 .env

Now create compose.yaml:

cat > compose.yaml <<'EOF'
services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      - "127.0.0.1:53:53/tcp"
      - "127.0.0.1:53:53/udp"
      - "127.0.0.1:8080:80/tcp"
    environment:
      TZ: "America/New_York"
      FTLCONF_webserver_api_password: "${PIHOLE_PASSWORD}"
      FTLCONF_dns_listeningMode: "ALL"
    volumes:
      - "./etc-pihole:/etc/pihole"
    restart: unless-stopped
EOF

Pi-hole requires FTLCONF_dns_listeningMode: "ALL" when it runs on Docker’s default bridge network, the private virtual network Docker creates for containers. This does not make your DNS public: in this configuration Docker still publishes the DNS ports only on the VPS loopback address 127.0.0.1.

Start Pi-hole:

docker compose up -d
docker compose ps

On the first start, Docker downloads the image and Pi-hole initializes its lists. After a short while, docker compose ps should report the container as healthy.

You can also check the DNS service directly:

docker exec pihole pihole status

The important lines should show that FTL, Pi-hole’s DNS engine, is listening on port 53 and that blocking is enabled.

Open the Web interface without exposing the administration port

The Web interface is deliberately bound to 127.0.0.1:8080. From your computer, open an SSH tunnel to the VPS:

ssh -L 8080:127.0.0.1:8080 root@YOUR_VPS_IP

Keep that SSH connection open, then visit http://127.0.0.1:8080/admin/ in your browser.

To display the password generated during installation:

cd /opt/pihole
grep '^PIHOLE_PASSWORD=' .env | cut -d= -f2-

Use that password on the Pi-hole login page. The dashboard then lets you follow queries, blocked domains, clients and filtering lists.

Check that blocking works

If dig is not available on your VPS yet, install it and send two queries directly to Pi-hole:

apt update
apt install -y dnsutils
dig @127.0.0.1 pi-hole.net +short
dig @127.0.0.1 doubleclick.net +short

The first command should return a normal IP address. With the current default blocklists, doubleclick.net is blocked and returns 0.0.0.0. If a list changes later, you can always confirm the result from Query Log in the Pi-hole interface.

Let your devices use Pi-hole

The Compose file deliberately keeps DNS on 127.0.0.1 during installation. After validation, there are three options:

  • Classic DNS: publish port 53 on the VPS IPv4 address only for source addresses allowed by your firewall.
  • Encrypted DNS: add a DoH/DoT component in front of Pi-hole, because Pi-hole does not provide these protocols natively.
  • Private network: keep DNS on a private address reachable through WireGuard or another VPN.

That extra layer for encrypted DNS is exactly why Pi-hole is less attractive than our AdGuard Home recommendation on a VPS.

Update Pi-hole

With Docker, updating remains straightforward:

cd /opt/pihole
docker compose pull
docker compose up -d

The /opt/pihole/etc-pihole directory keeps your configuration and data across container recreations. /opt/pihole/.env contains the administration password, so keep that file protected and do not share it.

Configure your filtering DNS on Windows, Android, iPhone and iPad

There are three approaches: classic DNS using the VPS IP address, encrypted DoH/DoT using a domain name when the software supports it, or a private network, such as WireGuard, if you prefer to keep DNS completely private.

Configure DNS on Windows, Android, iPhone and iPad

Which address should you enter?

If you use classic DNS, enter the public IPv4 address of your VPS, for example 203.0.113.10. Port 53 must then be reachable over UDP and TCP, the two transports used by classic DNS.

Do not leave port 53 open to the whole Internet. Restrict it in your firewall to the addresses or networks that are allowed to use it. On a fixed Internet connection, for example, you can allow only your current public IP address.

The Compose files in this guide deliberately start on 127.0.0.1. If you choose classic DNS, replace only the two DNS mappings with the actual public IPv4 address of your VPS, for example:

ports:
  - "203.0.113.10:53:53/tcp"
  - "203.0.113.10:53:53/udp"

Then apply the change with docker compose up -d, but only after the firewall restrictions are in place.

If your public address changes often, that restriction becomes less convenient. Mobile 4G/5G source addresses also change regularly, so DoH/DoT or WireGuard are better suited there.

If you use DoH or DoT, you will usually enter a name such as dns.example.com instead of the raw IP address. That name must point to the VPS and the service must have a valid TLS certificate.

Finally, if your DNS remains deliberately private on an address such as 10.66.66.1, your devices must join that private network, for example through WireGuard.

Windows 10 and Windows 11: simply change the DNS server

On Windows, no additional software is required to use classic DNS.

Open Settings > Network & Internet, select your Wi-Fi or Ethernet connection and open its properties. Depending on your Windows version, look for DNS server assignment or Edit IP settings, choose Manual, then enable IPv4.

Under Preferred DNS, enter your VPS address:

203.0.113.10

Leave the secondary DNS field empty, or use a second filtering DNS server that you control. If you enter an ordinary public resolver as the secondary server, some requests may bypass your filtering.

On Windows 11, you can also enable DNS over HTTPS if your server provides DoH. Windows 10 does not expose this DoH setting directly in the same interface.

Then check which DNS server is actually being used:

nslookup pi-hole.net
nslookup doubleclick.net

The Server line should point to your DNS server. Depending on the software, a blocked domain may return 0.0.0.0 or NXDOMAIN.

Android: use built-in Private DNS when DoT is available

Android includes a Private DNS setting that works on Wi-Fi and mobile data. It uses DoT, so your server must provide DNS-over-TLS.

Open Settings > Network & Internet > Private DNS. The exact path may vary slightly by manufacturer. Choose Private DNS provider hostname, then enter only your server name, for example:

dns.example.com

Do not enter https:// and do not enter only the IP address: Android expects the hostname of the DoT server here.

AdGuard Home and Technitium can provide DoT directly. Blocky can also serve DoT when configured for it. Pi-hole needs an additional component or a different method.

If you prefer DoH or want to manage DNS through an app, Rethink DNS is an open-source alternative. Its DNS mode can send requests to a user-selected DoH or DoT resolver. Add the DoH URL or DoT hostname of your own server.

For AdGuard Home, Android’s built-in Private DNS setting remains the simplest route once DoT is enabled on the VPS.

iPhone and iPad: native DNS settings for Wi-Fi

For a Wi-Fi network, iOS and iPadOS let you change the DNS server without installing an application.

Open Settings > Wi-Fi, tap the information button next to the connected network, then Configure DNS > Manual > Add Server.

Enter the IPv4 address of your VPS, for example:

203.0.113.10

Remove the old DNS servers from that configuration if you want to make sure requests use your filtering DNS, then save.

This setting applies to that configured Wi-Fi network. To apply encrypted DNS on 4G/5G and other networks too, DNSecure is an open-source app dedicated to DoH and DoT on iOS and iPadOS.

In DNSecure, add your own DoH or DoT server, enable Use This Server, then select DNSecure in the iOS or iPadOS DNS settings. Encrypted DNS then applies system-wide rather than only in Safari.

TVs, consoles and other devices

If the device lets you enter a DNS server manually, use the same logic as on Windows and enter your VPS IPv4 address for classic DNS, with the firewall properly restricted.

If it does not provide a useful DNS setting, your router may be able to distribute your DNS server to every device on the network. Look for the DNS setting of the DHCP server, the router service that automatically gives devices their IP address and network settings. With classic DNS, keep VPS port 53 restricted to the public IP address of your home connection.

Where does WireGuard fit in?

WireGuard remains useful if you want no DNS service exposed publicly at all. In that case, keep port 53 bound only to the VPS private address, for example 10.66.66.1, and connect the device to the WireGuard network. To set up this VPN, follow our guide Install a VPN on a Linux VPS: WireGuard vs OpenVPN.

The client configuration then includes entries such as:

[Interface]
DNS = 10.66.66.1

[Peer]
AllowedIPs = 10.66.66.0/24

The [Interface] section contains the device settings here. The [Peer] section describes the WireGuard server it connects to. AllowedIPs specifies which network addresses should go through the tunnel.

With this setup, only the private network goes through WireGuard. This is a split tunnel: normal Internet browsing still uses your usual connection, while DNS reaches the VPS through the private tunnel.

If you instead want to send all Internet traffic through WireGuard, use a full-VPN configuration with AllowedIPs = 0.0.0.0/0 and also configure routing and NAT (Network Address Translation) on the VPS, the mechanism that lets tunnel traffic leave through the server’s public address. That is not required just to benefit from DNS filtering.

Check that filtering works

After changing DNS, open a few websites and check your DNS software interface or logs: Pi-hole, AdGuard Home and Technitium show DNS queries, while Blocky exposes them through its logs and metrics.

If no queries arrive, first confirm that the service is listening on the address used by the device and that the firewall allows the relevant protocol: port 53 for classic DNS, 853 for DoT, or the HTTPS port configured for DoH.

A personal DNS can also bypass some ISP blocks

Another benefit of hosting DNS on your VPS is that you no longer have to rely on the resolver provided by your Internet service provider. When a website is blocked only at the DNS level, for example because the ISP resolver refuses to resolve its domain or returns a modified response, sending DNS requests to your own VPS can allow that domain to resolve normally.

This works as soon as the device actually queries your VPS instead of the ISP resolver. With classic DNS, those requests remain unencrypted in transit. DoH, DoT, or a private network such as WireGuard also protect that path against observation or modification of DNS queries.

However, a personal DNS server is not a VPN and cannot bypass every type of block. If filtering happens directly on the IP address, routing, or SNI, the site name exposed at the start of some TLS connections, or on another network layer, changing DNS will not be enough. DoH and DoT encrypt DNS queries to your VPS, not all of your Internet traffic.

Which filtering DNS should you choose for your Linux VPS?

For the vast majority of users: AdGuard Home. It is our default recommendation. Installation stays simple, the interface is clear, and DoH, DoT and DoQ are built in. You avoid stacking several applications just to get a modern filtering DNS service on your VPS.

Technitium: choose it when you want to go deeper into DNS. It is very powerful, but its many settings and full DNS-server features are unnecessary if your only goal is blocking ads.

Blocky: choose it if you prefer YAML and automation. It is lightweight and clean, but the lack of a full administration UI makes it better suited to DevOps users.

Pi-hole: our last choice for this VPS scenario. Its popularity and community remain excellent, but the lack of native server-side DoH/DoT means adding other components or a VPN. On a VPS, we only recommend it to advanced users. It makes more sense on a local network or for someone who specifically wants the Pi-hole ecosystem.

Whichever software you choose, the deployment logic is similar.

  1. Prepare a Linux VPS with a dedicated IPv4 address and root access.
  2. Install the software natively or through Docker.
  3. Keep the administration interface private or tightly restricted.
  4. Choose your access method: firewall-restricted classic DNS, DoH/DoT, or a private network such as WireGuard.
  5. Configure your blocklists and exceptions.
  6. Gradually point your devices to the new DNS server.
  7. Check from the Internet that your server does not accept classic recursive DNS queries from arbitrary addresses.
  8. Back up the configuration before major updates.

If you choose Docker, our Install Docker on Linux guide covers the VPS preparation you need before deploying your DNS service.

Filtering DNS does not completely replace a content blocker

DNS filtering works at the domain-name level. If tracker.example is blocked, the device cannot resolve that domain and the connection fails before it starts.

This is extremely effective against many trackers, advertising domains, telemetry services and malicious domains. It also works on devices where installing a browser extension is impossible.

However, DNS cannot see the full URL of an HTTPS request. If an ad and legitimate content are delivered from exactly the same domain, DNS cannot block one without blocking the other. That is one reason filtering DNS does not always replace a browser content blocker.

The best result is often to combine both approaches: DNS filtering for every device, then browser-level blocking when you need much finer control over a Web page.

If you want the simplest starting point on a VPS, AdGuard Home remains our default recommendation thanks to its interface and native support for DoH, DoT and DoQ. BoxToPlay Linux VPS plans include a dedicated IPv4 address, root access, a configurable firewall and Docker compatibility for hosting your filtering DNS service and other self-hosted tools.

Join the Discussion
🍪