Skip to content

Securely Access Your Server From Anywhere
(With DuckDNS, PiHole, LetsEncrypt & OpenVPN)

Last updated: May 2022. For advanced users. Solid tech skills required.

PiHole YouTube ads

After having successfully set up your Ubuntu server and configured basic as well as advanced security measures, this section will explain how to:

  • point a (sub-)domain to your home IP address with DuckDNS
  • encrypt traffic with Let's Encrypt
  • access local services, use a privacy respecting DNS provider and block ads with Pi-hole
  • shield the server from the Internet with a reverse proxy
  • securely access the server from anywhere in the world with OpenVPN

How to set up a DuckDNS domain


Human-readable addresses are easier to remember than random IPs. Rather than typing into their browser, most users prefer to navigate to For this to work with your server, you need a unique name which points to your home IP address, also known as domain name. In addition, a domain name is required to encrypt traffic, as explained in the next section.

DuckDNS allows to register a (sub-)domain for free and dynamically points it to your home IP address. This means it even works if your IP address changes quite often. Simply head over to DuckDNS and register a (sub-)domain of your choice. As soon as the registration is confirmed, you will receive a unique token.

For the purpose of this tutorial, we've registered the (sub-)domain and received the token ebaa3bd3-177c-4230-8d91-a7a946b5a51e. This will allow us later on to access services on the server via addresses such as

LetsEncrypt auto renew

LetsEncrypt, a trusted Certificate Authority

All traffic should be encrypted over HTTPS. The browser's address bar should always display a secure connection, no matter if you browse the Internet or access self-hosted services. This means you need SSL certificates – either buy them or get them for free from Let's Encrypt, a trusted Certificate Authority. More detailed instructions below.

Show me the step-by-step guide

Perform a LetsEncrypt DNS challenge with Dehydrated

Let's Encrypt needs to verify that you actually own the domain before delivering any certificate. 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!).

Caution: 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 commands 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 a LetsEncrypt wildcard certificate

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 certificate:

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/

LetsEncrypt renew certificate

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. Create a log file:

sudo touch /var/log/dehydrated

Next, create a weekly cron job which checks the certificate's validity and, if required, renews it:

sudo vi /etc/cron.weekly/dehydrated

Add the following content:


echo "Checking cert renewals at `date`" >> $MYLOG
/etc/dehydrated/dehydrated -c >> $MYLOG 2>&1

Save and close the file (:wq!). Now, make the script executable:

sudo chmod +x /etc/cron.weekly/dehydrated

Test the cronjob:

sudo bash /etc/cron.weekly/dehydrated
sudo cat /var/log/dehydrated

The terminal output should look something like:

Checking cert renewals at Sun 01 Jan 2021 01:29:06 PM CEST
# INFO: Using main config file /etc/dehydrated/config
Processing *
 + Checking domain name(s) of existing cert... unchanged.
 + Checking expire date of existing cert...
 + Valid till Mar 12 10:23:18 2021 GMT (Longer than 30 days). Skipping renew!
Show me the 2-minute summary video

What about 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.

This network is blocking encrypted DNS traffic

PiHole setup – block ads & choose upstream DNS servers

What does private DNS mean

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

Show me the step-by-step guide

Install PiHole

At the time of writing, 5.10 was the latest Pi-hole release. It's quite lightweight, 50 MB of free space and 512 MB 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 Select Yes, then hit ENTER to continue.
Choose An Interface Select your network interface. Press TAB to toggle the selection, SPACE 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.
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: 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:

PiHole update

Pi-hole/admin web interface behind an Apache Reverse Proxy

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

PiHole – change password, add PiHole block lists

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 end 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 the 3-minute summary video

Temporary workaround for Ubuntu 22.04

At the time of writing, Pi-hole 5.10 doesn't officially support Ubuntu 22.04. For this reason, the usual Pi-hole install script doesn't work correctly. As a temporary workaround, execute the following commands instead:

sudo bash
curl -sSL | PIHOLE_SKIP_OS_CHECK=true bash

Don't forget to exit the bash shell once the installation successfully completes. Simply type the following command:

What is private DNS?

Private DNS meaning

What is a DNS provider? 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 works a bit like the former telephone switchboards: each time you enter a website address, a DNS resolver looks up the corresponding IP 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 the DNS resolver you use — or rather, which has been imposed on you — 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 non-encrypted. Even if you fully trust your DNS resolver, without encrypted DNS traffic others could eavesdrop and try to manipulate you (spoofing attack).
Censorship Internet providers can also censor your online activities by tracking DNS queries.

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

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

How to block ads? Is Pi-Hole a YouTube ad blocker?

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. Note however that Pi-Hole doesn't block in-video ads on YouTube (for that, you'll need something like uBlock Origin).

Block lists Description
Default •
PiHole Adlists •
Tracking & telemetry •
Malicious sites •

What is a Reverse Proxy?

A Reverse Proxy is an application sitting between a service running on your server and the Internet. All services presented on our website, including Pi-hole, are set up as Apache Virtual Hosts behind a Reverse Proxy. This allows to:

  • prevent the server from being directly exposed to the Internet
  • run several services side by side on a single server
  • minimise the number of open ports (typically ports 80 and 443)
  • 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

In Ubuntu, Apache Virtual Host configuration files are located in the directory etc/apache2/sites-available. Here an Apache Virtual Host example:

<VirtualHost *:80>

    ServerName                             # address of the service
    ServerAlias                        # address of the 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.

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

Wireguard vs OpenVPN

Secure remote access with OpenVPN

At this stage, the server is only accessible from within your home network, since it's shielded from the Internet by a firewall. One solution to enable remote access would be to "poke" multiple holes into the firewall and forward ports for each service from your router to the server. This would however expose multiple ports to the Internet and provide attackers with a larger attack surface to force their way into your network.

A Virtual Private Network (VPN) allows to connect to the server from anywhere in the world while mitigating this risk. Only one single port is made public, and only pre-authorised connections are allowed, requiring both a certificate and a password. 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? Enter your public IPv4 address, which you can find for example on For this tutorial, let's assume your public IP address is 88.888.88.88 (adjust accordingly).
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.



The client configuration is available in: /root/computer_vpn.ovpn
New clients can be added by running 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: /root/phone_vpn.ovpn

Finally, move the certificate(s) to the gofossadmin home folder and set the correct permissions:

sudo mv /root/computer_vpn.ovpn .
sudo mv /root/phone_vpn.ovpn .
sudo chmod 755 *.ovpn
sudo chown gofossadmin:gofossadmin *.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

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

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 (put a hashtag # at the beginning of the respective line) 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 (adjust accordingly). Then navigate to Settings ‣ DNS ‣ Interface settings and select the option Permit all origins. Then click on Save.

Router settings

Check your router settings and make sure:

  • the port used by OpenVPN is forwarded correctly (in this example 1194, adjust accordingly). Refer to the router's manual for more information
  • the port 53 is closed. Scan for open ports using the tools of Gibson Research Corporation: select Proceed ‣ All service ports and make sure port 53 doesn't appear as open


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 (adjust accordingly) and retrieve all certificates from the server:

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

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 (cf. above). Copy the certificate from the desktop device to the mobile device (in this example, the certificate is named phone_vpn.ovpn, adjust accordingly).

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 (in this example phone_vpn.ovpn) and import it.

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

Who is my DNS provider

OpenVPN & PiHole test

Now that everything is set up, traffic should be handled as follows:

  • Each time one of your devices looks up an address, it securely connects to the server via VPN
  • If the address contains ads or trackers, Pi-hole blocks those elements
  • If the address points to a self-hosted service, Pi-hole redirects the request to the service on the server
  • If the address points to an external website or service, Pi-hole redirects the request to an upstream DNS server of your choice (e.g. Digitalcourage, UncensoredDNS, etc.)
  • All traffic is encrypted via HTTPS

Make sure everything runs smoothly:

  • browse to or an verify if ads are blocked. You can also browse to (adjust accordingly) and check the dashboard for blocked queries
  • browse to a local service, such as (adjust accordingly), and verify the address is resolved correctly
  • browse to or and make sure you're using the correct upstream DNS resolver

Attack surface management


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.

Private DNS provider hostname