How to install Pi-hole on Ubuntu 20.04 on an AWS EC2 instance

I first heard about Pi-hole a couple of years ago, discussing ad-blockers with a friend. It sounded interesting but totally forgot about it until a last weekend, when I saw someone mentioning it on Reddit, so I finally decided to give it a go.

To try this I used an t3a.nano EC2 instance, the smallest available, and so far it’s been more than enough.

One little thing before we start. Since this was an experiment to try Pi-hole I didn’t spend time automating it. All these steps will be done manually. I might automate it in the future, but not yet.

What are we going to do then? First of all spin up an EC2 instance where we can install Pi-hole, configure its security groups and so on. Then we will install Pi-hole and setup a domain name pointing at our instance. Then, we will enable SSL to connect to the admin panel. Even though we’ll restrict access through the security groups it’s always a good idea, and Let’s Encrypt is free 🙂

Preparing the EC2 instance

Launching a new instance

  • On the AWS console go to EC2 ➡️ Instances ➡️ Launch instances.
  • Select Ubuntu Server 20.04 – 64-bit
  • Choose whatever instance size you think is best. The minimum requirements for Pi-hole are:
    • 2GB of free space, 4GB recommended
    • 512GB RAM
  • I’m using a t3a.nano and for my level of usage it’s more than enough. The t3a family uses AMD processors and it’s around 10% cheaper than its Intel counterpart.
  • In the next section select the appropriate values for your VPC, subnet, etc.
  • Here you can also create an IAM role and assign it to the instance. We will use this later to interact with Route53, but I’ll cover it in the Let’s Encrypt section of the post.
  • It’s time to add storage. AWS assigns 8GB by default. I used 20.
  • Add tags if you want to.
  • Let’s configure our security group. The ports that Pi-Hole uses are:
ServicePortProtocolNotes
pihole daemon53 (DNS)TCP/UDP
pihole daemon67 (DHCP)IPv4 UDPThis is an optional feature. I’m not using it so I didn’t open it.
pihole daemon547 (DHCPv6)IPv6 UDPThis is an optional feature. I’m not using it so I didn’t open it.
lighttpd80 (HTTP)TCPAccess to the admin panel
lighttpd443 (HTTPS)TCPAccess to the admin panel over HTTPS
  • If we look at this from the Security Group’s perspective
  • Before you launch your new instance you’ll need to select (or create) a key pair to access it.

Adding a public IP

Now that our instance is ready we need to add a public IP so we can access it from outside the VPC. You might not need to do this if, for example, you have a VPN to AWS.

  • In the EC2 section, go to Network & Security ➡️ Elastic IPs.
  • After it’s been allocated we need to associate it with our EC2 instance.

Installing Pi-hole on Ubuntu 20.04

Pi-hole’s documentation lists three different options to install it. The first one is a one-step automated install. They do this by piping a script to bash, and since this is a controversial topic they provide two other alternatives. You can find them here.

curl -sSL https://install.pi-hole.net | bash

The install process will take you through several steps. Your first choice is which Upstream DNS Provider you want to use. In my case I didn’t want to use just Google, Cloudflare or other, so I chose Custom.

And used 8.8.8.8 (Google) and 1.1.1.1 (Cloudflare).

The next step is selecting the lists that will be used to block ads. I left the two set by default. We will cover how to add others later.

Used the default values to block adds over both IPv4 and IPv6.

You can leave the default values set for the IP address and Gateway.

This is an important step if you want to follow this tutorial later. We need to install the web admin interface.

And the lighttpd (webserver). Otherwise the admin interface won’t work.

The next steps are related to logging the queries or not. Since I just wanted to try it, I used the default values.

At the end of this process you will see a message with the admin password. Save it, you will need it later.

Point a subdomain to our EC2 instance

Since we’re working on AWS, I will use Route 53. If you are not using it the next steps and many of the steps related to SSL automation won’t apply to you.

  • Take note of the public IP address of your instance.
  • Go to Route 53 and find the hosted zone related to the domain name you want to use.
Select create new record
Use Simple routing
Set the public IP of your EC2 instance here

Since your security group is already configured to allow access using port 80. Once the DNS changes have propagated you should be able to access the admin panel on:

http://yoursubdomain.example.com/admin/

Enabling SSL for the admin panel

Time to make this a wee bit more secure using SSL. I like using the DNS challenge when working with Let’s Encrypt, and it’s very easy to automate with Route 53. If you’d rather use a different challenge, some of these steps will not be relevant for you.

Creating an IAM role for our EC2 instance

As I mentioned above, there’s a very easy way to automate the DNS challenge with Certbot and Route 53. However, to do so we need to have permissions to edit our preferred hosted zone.

There are many different ways to do it. For instance, we could create an IAM user and save its credentials on our EC2 box (bad idea!). The way AWS recommends is using a role that the applications running on our instance can assume.

If you want to know more about IAM roles for EC2, you can check the documentation.

  • To create the role, go to IAM ➡️ Roles ➡️ Create role
  • Then, select EC2 as your trusted entity
  • You will need to create a policy (or you can add one later as an inline policy, up to you). But the policy should be:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "route53:ListHostedZones",
                "route53:GetChange"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "route53:ChangeResourceRecordSets"
            ],
            "Resource": "arn:aws:route53:::hostedzone/HOSTEDZONEID"
        }
    ]
}
  • After you have created your new role you’ll need to add it to the Pi-hole EC2 instance.
  • Go to EC2 ➡️ Instances ➡️ Your Instance
  • And select the role you just created.

Installing Certbot on Ubuntu 20.04

It’s time to SSH back onto our instance.

  • We need to install two packages.
sudo apt-get install certbot \
                     python3-certbot-dns-route53

Generating an SSL certificate with Certbot / Let’s Encrypt

  • And now let’s create our certificate
sudo certbot certonly --dns-route53 -d yoursubdomain.example.com

Certbot will ask you some details, like your email address, and it will proceed to generate your certificate. The result will show something like this:

And that’s our certificate created. You can find it under/etc/letsencrypt/live/yoursubdomain.example.com/

Configuring SSL on lighttpd

Let’s finally enable SSL now. You’ll need to edit /etc/lighttpd/external.conf Your file should look like this:

$HTTP["host"] == "yoursubdomain.yourdomain.com" {
  # Pi-hole needs to know that this is not a blocked domain
  setenv.add-environment = ("fqdn" => "true")
  # Enable SSL using our Let's Encrypt certificate only for this host
  $SERVER["socket"] == ":443" {
    ssl.engine = "enable"
    ssl.pemfile = "/etc/letsencrypt/live/xxxxxxxxxx/cert.pem"
    ssl.ca-file =  "/etc/letsencrypt/live/xxxxxxxxxx/fullchain.pem"
    ssl.privkey = "/etc/letsencrypt/live/xxxxxxxxxx/privkey.pem"
    ssl.honor-cipher-order = "enable"
    ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
    ssl.use-sslv2 = "disable"
    ssl.use-sslv3 = "disable"
  }
  # Enable HTTP to HTTPS redirection
  $HTTP["scheme"] == "http" {
    $HTTP["host"] =~ ".*" {
      url.redirect = (".*" => "https://%0$0")
    }
  }
}

Restart lighttpd:

systemctl restart lighttpd

And that’s it! The admin panel should now be available over HTTPS.


Thanks again for joining me! I hope this has been useful. See you next time.

2 comments

    1. Hi Robi,

      Yes, you can connect to the admin panel without HTTPS. You have some options:

      – If you skip the whole section #3 you won’t enable HTTPs but should be able to access it over HTTP on port 80.
      – If you don’t add lines 15-20 to the lighttpd config you should be able to access it over both HTTP and HTTPS.

      Does this answer you question?

      Cheers!

  1. hello ,
    when I was doing this tutorial this accrued to me could you help me with it

    An error occurred (AccessDenied) when calling the ChangeResourceRecordSets operation: User: arn:aws:sts::000773316215:assumed-role/ppp/i-03e2a9aad38aa251a is not authorized to perform: route53:ChangeResourceRecordSets on resource: arn:aws:route53:::hostedzone/Z0851402WG9ORG02A650
    To use certbot-dns-route53, configure credentials as described at https://boto3.readthedocs.io/en/latest/guide/configuration.html#best-practices-for-configuring-credentials and add the necessary permissions for Route53 access.

Leave a Reply

Your email address will not be published. Required fields are marked *