Skip to content

This chapter is geared towards advanced users. Solid tech skills are required.

Secure domain — How to set up DuckDNS, Let's Encrypt, Pi-Hole and OpenVPN

Install Pi-Hole

This section explains how to shield your server from the Internet, using HTTPS, Pi-hole, a privacy respecting DNS provider and VPN. Once set up, traffic will be handled as follows:

  • Access to and from websites or web services: Your devices connect to the server via VPN. Pi-Hole redirects requests via an upstream DNS provider of your choice. Ads and trackers are blocked.
  • Access to and from self-hosted services: Your devices connect to the server via VPN. Various services such as file sync, photo galleries, task lists, media streaming and so on are accessible through local addresses such as All traffic is HTTPS encrypted.



Human-readable addresses are easier to remember than random IPs. Rather than typing into their browser, most users prefer to navigate to For this simple reason, we're going to set up local addresses for each self-hosted service:,, and so on.

For this to work, you need a unique name which identifies your services, also known as domain name. You can register one for free at DuckDNS. As soon as your registration is confirmed, you will receive a unique token. For the purpose of this tutorial, we've registered the domain name with the token ebaa3bd3-177c-4230-8d91-a7a946b5a51e.

I only want to access my services from within my home network. Do I still need a domain name?

Yes, a domain name is required to set up HTTPS encryption. You can of course register any available domain name you like. Just make sure to adapt the relevant commands accordingly.

Let's Encrypt certificate

Let's Encrypt

We want all traffic to be encrypted over HTTPS. The browser's address bar should always display a secure connection, no matter if you surf on the Internet or access self-hosted services. This means you need SSL certificates from a trusted Certificate Authority, such as Let's Encrypt. More detailed instructions below.

Show me the step-by-step guide

Install Dehydrated

Let's Encrypt needs to verify if you actually own the domain before any certificate can be delivered. A nifty program called Dehydrated makes this possible. Log into the server and download Dehydrated:

sudo apt install git
git clone

Navigate to the directory dehydrated and create a first configuration file:

cd dehydrated
vi domains.txt

Add the following line, and make sure to provide your own domain name:

* >

Save and close the file (:wq!), then create a second configuration file:

vi config

Add the following lines. Make sure to provide your own email address:


Save and close the file (:wq!), then create a third configuration file:


Add the following lines. Make sure to provide your own domain name, as well as your own token delivered by DuckDNS:

DOMAIN=""                         # provide your domain here
TOKEN="ebaa3bd3-177c-4230-8d91-a7a946b5a51e"        # provide your token here

case "$1" in
        curl "$DOMAIN&token=$TOKEN&txt=$4"
        curl "$DOMAIN&token=$TOKEN&txt=removed&clear=true"
        echo Unknown hook "${1}"
        exit 0

Save and close the file (:wq!).

Using a different domain than DuckDNS?

Above instructions need to be modified if you work with another domain than DuckDNS. Please refer to Dehydrated's documentation or Let's Encrypt's community to properly set up the hook for DNS-based validation.

The next set of commmands creates a directory where all SSL certificates will be stored, called certs. The files dehydrated and are made executable. The dehydrated directory moves to /etc/dehydrated. Finally, gofossadmin is given the ownership of the directory /etc/dehydrated. Make sure to adjust the administrator name to your own setup:

mkdir certs
chmod a+x dehydrated
chmod a+x
cd ..
sudo mv dehydrated /etc/dehydrated
sudo chown -R gofossadmin:gofossadmin /etc/dehydrated

Check the directory layout:

sudo ls -al /etc/dehydrated

The output should look something like:

drwxr-xr-x 2 gofossadmin gofossadmin   4096 Jan 01 00:00 .
drwxr-xr-x 4 gofossadmin gofossadmin   4096 Jan 01 00:00 ..
-rw-r--r-- 1 gofossadmin gofossadmin    200 Jan 01 00:00 certs
-rw-r--r-- 1 gofossadmin gofossadmin    200 Jan 01 00:00 config
-rwxr-xr-x 1 gofossadmin gofossadmin 700000 Jan 01 00:00 dehydrated
-rw-r--r-- 1 gofossadmin gofossadmin    200 Jan 01 00:00 docs
-rw-r--r-- 1 gofossadmin gofossadmin     20 Jan 01 00:00 domains.txt
-rwxr-xr-x 1 gofossadmin gofossadmin    700 Jan 01 00:00

Create certificates

Register with Let's Encrypt:

bash /etc/dehydrated/dehydrated --register --accept-terms

This command creates a directory /etc/dehydrated/accounts, containing your registration information. The terminal should show something like:

# INFO: Using main config file /etc/dehydrated/config
+ Generating account key...
+ Registering account key with ACME server...
+ Done!

Now, create the SSL certificates:

bash /etc/dehydrated/dehydrated -c

The terminal should prompt something like:

+ Creating chain cache directory /etc/dehydrated/chains
+ Creating new directory /etc/dehydrated/certs/ ...
+ Signing domains...
+ Generating private key...
+ Generating signing request...
+ Requesting new certificate order from CA...
+ Received 1 authorizations URLs from the CA
+ Handling authorization for
+ 1 pending challenge(s)
+ Deploying challenge tokens...
+ Responding to challenge for authorization...
+ Challenge is valid!
+ Cleaning challenge tokens...
+ Requesting certificate...
+ Checking certificate...
+ Done!
+ Creating fullchain.pem...
+ Done!

That's it! Two SSL keys have been created to encrypt all traffic to and from your self-hosted services. We'll explain later how to use these keys.

File Location
Public key /etc/dehydrated/certs/
Private key /etc/dehydrated/certs/

Autorenew certificates

Let's Encrypt's certificates expire after 90 days, and can be renewed 30 days before expiry. It's a good idea to automate these updates with cron:

crontab -e

Add the following line at the end of the file. It's a scheduled task which checks the certificate's validity on the first day of every month at 8:00am and, if required, renews it:

0 8 1 * * root bash /etc/dehydrated/dehydrated -c
Show me a summary video

Wouldn't it be easier to use self-signed certificates?

Using self-signed certificates for HTTPS encryption is possible. That said, they often won't be recognised by browsers and applications, or generate warnings and error messages. On Android phones for example, self-signed certificates must first be imported and trusted. For these reasons, we suggest to privilege a Certificate Authority like Let's Encrypt.



Pi-hole interface

Pi-hole is a nifty tool: it lets you pick a DNS provider of your choice, is capable of resolving local addresses such as and blocks ads and trackers. Follow the instructions below to set up Pi-Hole on your server.

Show me the step-by-step guide

Pi-Hole installation

At the time of writing, 5.2 was the latest Pi-Hole release. It's quite lightweight, 50MB of free space and 512MB of RAM are enough to run it on Ubuntu. Pi-hole also requires a static IP address as well as Apache and PHP, which we configured in previous chapters. Run Pi-hole's install script:

sudo curl -sSL | bash

Follow the on-screen instructions:

Instruction Description
This installer will transform your device into a network-wide ad blocker! Hit ENTER to continue.
The Pi-hole is free, but powered by your donations Hit ENTER to continue.
The Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly Hit ENTER to continue.
Choose An Interface Select your network interface. Press SPACE to toggle the selection, TAB to confirm your selection, and ENTER to continue.
Select Upstream DNS Provider. To use your own, select Custom Select a DNS provider of your choice. In this example, we'll use UncensoredDNS, but feel free to select any other DNS provider! We suggest to choose a DNS provider other than your ISP, Cloudfare or Google. Learn more about DNS providers at the end of this section.
Pi-hole relies on third party lists in order to block ads. Hit ENTER to continue. We'll explain later how to add more block lists.
Select Protocols Choose your protocol. IPv4 and IPv6 should be selected by default. Hit OK to continue.
Do you want to use your current network settings as a static address? In a previous section, we set up a static IP address for the server. In our example, it's, adjust this to your own configuration. Hit TAB to select Yes and ENTER to continue.
It is possible your router could still try to assign this IP to a device, which would cause a conflict Read the warning, and hit ENTER to continue.
Do you wish to install the web admin interface? Select On if you want a web interface (recommended), and hit ENTER to continue.
Do you wish to install the web server (lighttpd)? We'll use Apache as web server, instead of lighttpd. Select Off and confirm with SPACE, then hit ENTER to continue.
Do you want to log queries? Select On (recommended) and hit ENTER to continue.
Select a privacy mode for FTL Select the privacy level of your choice, and hit ENTER to continue:

Show everything: shows records everything; provides the maximum amount of statistics.
Hide domains: displays and stores all domains as "hidden"; disables the Top Domains and Top Ads tables on the dashboard.
Hide domains and clients: displays and stores all domains as "hidden" and all clients as ""; disables all tables on the dashboard.
Anonymous mode: disables basically everything except the live anonymous statistics; no history is saved to the database, nothing is shown in the query log, there are no top item lists. Provides enhanced privacy.
Installation Complete! Once the installation completes, you should see this:

Pi-hole installation

Web interface

We're going to set up an Apache Virtual Host as a Reverse Proxy to access the Pi-hole web interface. Sounds complex, but in essence a reverse proxy further shields the server from the Internet. You can read more about it at the end of this section.

Set the right permissions:

sudo chown www-data:www-data -R /var/www/html/admin/
sudo usermod -aG pihole www-data
sudo chown -R pihole:pihole /etc/pihole

Create an Apache configuration file:

sudo vi /etc/apache2/sites-available/

Add the following content and make sure to adjust the settings to your own setup, such as domain names (, path to SSL keys, IP addresses and so on:

<VirtualHost *:80>

    Redirect permanent /


<VirtualHost *:443>

    ServerSignature         Off

    SSLEngine               On
    SSLProxyEngine          On
    SSLProxyCheckPeerCN     Off
    SSLCertificateFile      /etc/dehydrated/certs/
    SSLCertificateKeyFile   /etc/dehydrated/certs/
    DocumentRoot            /var/www/html/admin

    <Location />
        Order deny,allow
        Deny from all
        Allow from
        Allow from
        Allow from

    <Directory /var/www/html/admin/>
        Options +FollowSymlinks
        AllowOverride All
        Require all granted

    ErrorLog ${APACHE_LOG_DIR}/
    CustomLog ${APACHE_LOG_DIR}/ combined


Once the content is added, save and close the file (:wq!). Note how we enable SSL encryption with the instruction SSLEngine On, and use the SSL certicate /etc/dehydrated/certs/ as well as the private SSL key /etc/dehydrated/certs/, which were created earlier on.

Next, enable the Apache Virtual Host (adjust the domain accordingly):

sudo a2ensite
sudo systemctl reload apache2

Restart Pi-hole and check if everything runs correctly. The output should display "Active":

sudo systemctl restart pihole-FTL
sudo systemctl status pihole-FTL

Pi-Hole configuration

Change the default password for Pi-Hole's web interface. Provide a strong, unique password:

sudo pihole -a -p

Browse to and login with the new credentials. Make sure to adjust the server's IP address to your own setup. The web interface should show on the screen. It provides various settings, which are further described on Pi-Hole's website:

Feature Description
Gravity Browse to Tools ‣ Update Gravity to fetch the latest block lists. Be patient, it can take some seconds.
Dashboard Displays statistics: how many domains have been visited or blocked, how many domains are on the blocklist, and so on.
Queries Detailed information on queries.
Blocklists Navigate to Group management ‣ Adlists to add more blocklists and filter additional ads and malware. You'll find more information about Pi-hole Blocklists at the of this section.
Settings Easily manage and configure Pi-hole: DNS servers, privacy, etc.
Local DNS Pi-hole is capable of resolving local addresses. Navigate to Local DNS ‣ DNS Records and add the following domain/IP combination (adjust according to your own setup):

IP Address:

Show me a summary video

Tell me more about DNS and privacy

On the Internet, each computer has its own IP address. Since these IP addresses aren't easy to remember, the Domain Name System (DNS) was invented. It's like the phone book of the Internet: each time you visit a website, a DNS resolver looks up the corresponding IP address and connects your device to the appropriate computer/server. DNS therefore plays a key role when browsing the Internet. But since everything happens automatically, users often forget that DNS queries are an inherent risk to their privacy:

Risks Description
Logging DNS resolvers log every site you visit. Depending on your current DNS resolver, Google, Cloudflare, your Internet Service Provider (ISP), your telecom provider or any other third-party sees and stores a list of all websites you visit. Whether or not the traffic is encrypted over HTTPS doesn't matter.
No encryption DNS queries are mostly unencrypted. Even if you fully trust your DNS resolver, others could eavesdrop and try to manipulate you (spoofing attack).
Censorship Providers can also censor online activities by tracking DNS queries.

Pi-hole let's you choose which DNS resolver to use when browsing the Web. Here some privacy-aware DNS providers:

DNS provider Country DNS #1 DNS #2
Digitalcourage Germany --
UncensoredDNS Denmark
Dismail Germany
DNS Watch Germany
FDN France
OpenNIC Various Various Various

Tell me more about blocking ads and malware with Pi-Hole

Pi-Hole filters web traffic and blocks ads or trackers on your devices, without the need for any additional software. By navigating to Pi-Hole's web interface, you can add blocklists to filter additional ads and malware:

Block lists Description
Default •
Advertisement •
Tracking & telemetry •
Malicious sites •

Tell me more about Pi-Hole terminal commands

Most of Pi-Hole's configuration can be performed on the web interface. Alternatively, you can also configure Pi-Hole directly on the server, via the terminal. The full list of commands can be found on Pi-Hole's website:

Command Description
pihole status status
pihole -t tail log real time log
pihole -c statistics
pihole -w -l list whitelisted domains
pihole -w add to whitelist
pihole -w -d remove from whitelist
pihole -b -l list blacklisted domains
pihole -b add to blacklist
pihole -b -d remove from blacklist
pihole -up update pihole
pihole -l off query logging off
pihole -l on query logging on
pihole enable enable pihole
pihole disable disable pihole
pihole disable 10m disable pihole for 10 minutes
pihole disable 60s disable pihole for 60 seconds
pihole uninstall uninstall pihole

Tell me more about Apache Virtual Hosts and Reverse Proxies

All services suggested in this guide, including Pi-Hole, are set up as Apache Virtual Hosts behind a Reverse Proxy. This allows to:

  • run several services side by side on a single server
  • reach services over custom addresses, rather than random IP addresses and port numbers
  • easily manage SSL certificates
  • configure distinct security policies, such as HTTP headers, user authentication, access restrictions, and so on
  • prevent the server from being directly accessed from the Internet
  • minimise the number of open ports (typically ports 80 and 443)

In Ubuntu, Apache Virtual Host configuration files are located in the directory etc/apache2/sites-available. Their structure more or less looks like this:

<VirtualHost *:80>

    ServerName                             # address of the service
    ServerAlias                        # address of thee service, including www subdomain
    Redirect permanent /                # redirection of all non-encrypted http:// traffic to encrypted https:// traffic


<VirtualHost *:443>

    ServerName                             # address of the service
    ServerAlias                        # address of the service, including www
    ServerSignature         Off                                 # hide server information

    SSLEngine               On                                  # use SSL/TLS protocol
    SSLProxyEngine          On                                  # use SSL/TLS protocol for proxy
    SSLCertificateFile      /path/to/fullchain.pem              # location of the SSL certificate file in PEM format
    SSLCertificateKeyFile   /path/to/privkey.pem                # location of the PEM-encoded private SSL key file

    DocumentRoot            /var/www/example                    # directory from which Apache will serve files

    <Location />                                                # limit access to server, home network (LAN) and VPN
        Order deny,allow
        Deny from all
        Allow from
        Allow from
        Allow from

    <Directory /var/www/example>
        Options -Indexes +FollowSymLinks                        # prevent directory listings and follow symbolic links
        AllowOverride All                                       # directives from .htaccess file which can override configuration directives
        Require all granted

    ErrorLog ${APACHE_LOG_DIR}/            # log files
    CustomLog ${APACHE_LOG_DIR}/ combined # log files


The Virtual Host is enabled with sudo a2ensite Check if there are any syntax errors with sudo apachectl configtest.

Install OpenVPN


As explained previously, Pi-Hole allows you to access local addresses such as, to use a privacy respecting DNS provider and to browse the Internet without ads. For this to work, your devices need to be able to talk to Pi-Hole, which runs on your server. This is not yet possible, because the server is shielded from the Internet by a firewall. We are therefore going to set up a Virtual Private Network (VPN) and open one single port in the firewall to be able to access the server from anywhere in the world. Read on below for detailed instructions.

Show me the step-by-step guide

OpenVPN installation

OpenVPN is open source and uses OpenSSL, TLS and many security features. It can be set up with an easy install script:

wget -O
sudo bash

Follow the on-screen instructions:

Prompt Instructions
What is the public IPv4 address or hostname? Check your public IPv4 address on Let's assume our public IP address is 88.888.88.88 (adjust accordingly).
What IPv6 address should the OpenVPN server use? Select the first IPv6 address.
Which protocol do you want for OpenVPN connections? Choose the recommended UDP protocol.
What port do you want OpenVPN listening to? Choose a port, for the purpose of this tutorial we'll use the standard port 1194.
Which DNS do you want to use with the VPN? Since we're using Pi-Hole as DNS server, select the option Current system resolvers.
Finally, tell me your name for the client certificate. Choose a name for your first VPN client certificate. For the purpose of this tutorial, we'll create a certificate labelled computer_vpn, adjust accordingly.

Once the installation completes, the terminal should look similar to this:

Welcome to this OpenVPN road warrior installer!

I need to ask you a few questions before starting setup.
You can use the default options and just press enter if you are ok with them.

This server is behind NAT. What is the public IPv4 address or hostname?
Public IPv4 address / hostname [88.888.88.88]: 88.888.88.88

What IPv6 address should the OpenVPN server use?
    1) 1a00:a0a:10a:10a0:a00:00ff:faf0:a011
    2) 1a00:a0a:10a:10a0:a00:00bb:faf0:a022
IPv6 address [1]: 1

Which protocol do you want for OpenVPN connections?
1) UDP (recommended)
2) TCP
Protocol [1]: 1

What port do you want OpenVPN listening to?
Port [1194]: 1194

Which DNS do you want to use with the VPN?
1) Current system resolvers
3) Google
4) OpenDNS
5) NTT
6) AdGuard
DNS [1]: 1

Finally, tell me a name for the client certificate.
Client name [client]: computer_vpn

We are ready to set up your OpenVPN server now.



Your client configuration is available at: /home/gofossadmin/computer_vpn.ovpn
If you want to add more clients, just run this script again!


A first VPN client certificate, computer_vpn.ovpn, has been generated during the installation of OpenVPN. Create as many certificates as needed by re-running the install script, one for each device connecting to the server: desktop computers, laptops, tablets, phones, etc.

For the purpose of this tutorial, we'll create a second certificate called phone_vpn.ovpn. Of course, you can choose any name for the certificates. Just make sure to adjust the commands accordingly:

sudo bash

The terminal should display something similar to:

Looks like OpenVPN is already installed.

What do you want to do?
1) Add a new user
2) Revoke an existing user
3) Remove OpenVPN
4) Exit
Select an option: 1

Tell me a name for the client certificate.
Client name: phone_vpn


Write out database with 1 new entries
Data Base Updated

Client phone_vpn added, configuration is available at: /home/gofossadmin/phone_vpn.ovpn

Finally, set the correct permissions for the certificate(s):

sudo chmod 755 computer_vpn.ovpn
sudo chmod 755 phone_vpn.ovpn

OpenVPN configuration

Open port 1194 (UDP), or whatever port you specified during the installation of OpenVPN:

sudo ufw allow 1194/udp

Check if the port has been successfully added to the firewall rules:

sudo ufw status numbered

Also check your router settings and make sure the port used by OpenVPN is forwarded correctly. Refer to the router's manual for more information.

Confirm whether the virtual interface tun0 works, and get the name of the default subnet:

ip ad | grep tun0 

Get the OpenVPN server's IP address:

ip route | grep tun0

Make sure traffic is routed via the VPN tunnel:

sudo apt install traceroute

Next, we are going to tell clients which connect to the VPN to use Pi-Hole as primary DNS server. Edit the OpenVPN configuration file:

sudo vi /etc/openvpn/server/server.conf

Comment out all existing dhcp-option settings and replace them with the tun0 interface:

#push "dhcp-option DNS XX.XX.XXX.XXX"
#push "dhcp-option DNS XX.XX.XXX.XXX"
push "dhcp-option DNS"

Restart the OpenVPN server:

sudo systemctl restart openvpn-server@server

Browse to the Pi-Hole web interface, in this example Then navigate to Settings ‣ DNS ‣ Interface listening behavior and select the option Listen on all interfaces, permit all origins.


Only devices with valid certificates can establish a VPN connection to securely access self-hosted services. Here is how to transfer the previously generated certificates from the server to the devices.

We assume the client device runs Ubuntu/Linux and can establish a remote SSH login. Open a terminal on the client device, switch to the administrator user and retrieve all certificates from the server:

su - gofossadmin
scp -v -P 2222 gofossadmin@*.ovpn .

Add OpenVPN to the network manager:

sudo apt install network-manager-openvpn
sudo apt install network-manager-openvpn-gnome
sudo service network-manager restart

Finally, configure the device's network connection:

Step Instruction
1 Click on the Wifi icon in the top bar.
2 Click on Settings.
3 Navigate to the Network menu entry.
4 Click on the + icon in the VPN section.
5 Select Import from file.
6 Browse to the certificate (in this example computer_vpn.ovpn) and click on Open.
7 Click on the Wifi icon in the top bar.
8 Enable the VPN connection computer_vpn.

That's it! Your desktop device can securely connect to the server via VPN, from anywhere in the world.

Open F-Droid on your mobile device and install OpenVPN for Android.

Connect the mobile device via USB to the desktop device with which you retrieved all certificates from the server. Copy the certificate from the desktop device to the mobile device (in this example phone_vpn.ovpn).

Finally, open the OpenVPN app on your mobile device and configure it:

Step Instruction
1 Tap on + and then Import.
2 Browse to the certificate and import it.

That's it! Your mobile device can securely connect to the server via VPN, from anywhere in the world.

Final checks

Final checks

  • Test if local addresses are resolved correctly by browsing to from your client devices (adjust accordingly).

  • Test if you're using the correct DNS resolver, for example with or

  • Test if ads are blocked correctly by browsing to or You can also browse to (adjust accordingly) and check the dashboard for blocked queries.



For further details or questions, refer to Pi-hole's documentation, Let's Encrypt's documentation, OpenVPN's documentation or ask the Pi-hole community, the Let's Encrypt community or the OpenVPN community for help.

Google announcement