Let’s Encrypt UniFi

How To Install a Let’s Encrypt SSL Certificate on UniFi

Let’s Encrypt allows you to have a FREE signed SSL certificate on your UniFi Controller without having to spend any money.  When done correctly, the Let’s Encrypt certificate will continuously renew, and you will no longer have any security warnings in the browser bugging you about insecure HTTPS.

This article is based on my 15 Minute Hosted UniFi Controller setup, so start with that article, and then come back to this article when your UniFi Controller is up and running.  One additional step is that you need to create a DNS A Record that points to the IP address of your UniFi Controller.  Something like unifi.company.com.  That should be done first in order to ensure that DNS has time to propagate before you need to create the Let’s Encrypt certificate.

The first thing we have to do is to open up HTTP port 80 and HTTP port 443 so that Let’s Encrypt can renew itself.  If anyone browses directly to those services, they will get a connection refused response.

Log into your UniFi controller and run the following commands to allow those ports through the firewall:

sudo ufw allow 80/tcp

sudo ufw allow 443/tcp

Note that it would be better for security if you could lock these rules down to the FQDN’s that Let’s Encrypt requests are coming from (outbound1.letsencrypt.org and outbound2.letsencrypt.org), however it is not possible to use FQDN in iptables rules.  It *should* technically be possible to create a script that periodically checks the IP address resolution for those FQDN’s and updates the iptables rules with any changes to the IP addresses for Let’s Encrypt.  If anyone creates such a script, Contact Me and I’ll post an update!

Next, let’s install Let’s Encrypt:

sudo apt-get update

sudo apt-get install letsencrypt

Now we need to generate our certificates.

sudo letsencrypt certonly

This command will ask for your email and FQDN – it will also have you accept the terms of usage.  When complete, you should get a message that says ‘Congratulations!  Your certificate and chain….’  This means you successfully created the certificate.

A developer named Steve Jenkins create a really great script that automates the rest of the process, making it super easy.  So, thanks to Steve, and let’s download his script and modify a few settings.

sudo wget https://raw.githubusercontent.com/stevejenkins/unifi-linux-utils/master/unifi_ssl_import.sh -O /usr/local/bin/unifi_ssl_import.sh

sudo chmod +x /usr/local/bin/unifi_ssl_import.sh

Next, edit the /usr/local/bin/unifi_ssl_import.sh file that we imported:

sudo nano -w /usr/local/bin/unifi_ssl_import.sh

Find the line that says ‘UNIFI_HOSTNAME’ and change it to your own FQDN:


Next, since we are on a Ubuntu Digital Ocean droplet instead of AWS (which the script was based on), we need to comment out the AWS stuff and uncomment the Debian/Ubuntu stuff:

# Uncomment following three lines for Fedora/RedHat/CentOS

# Uncomment following three lines for Debian/Ubuntu

Next, enable Lets Encrypt mode:


Save and exit nano by doing CTRL+X followed by Y.

Finally, run the script!

sudo /usr/local/bin/unifi_ssl_import.sh

Now, this is great, and if you now close your browser and then re-open it to https://unifi.company.com:8443, you should no longer have the security warnings, and you will have a valid HTTPS certificate installed with a green padlock.  BUT – one caveat with Let’s Encrypt is that it expires pretty quickly (every 90 days if I remember correctly), so we also want to automate the process of renewing the certificate periodically.  To do this, we need to add a couple of lines to our /etc/crontab file, which will process these commands automatically on a schedule that we set (in this case, every 12 hours):

sudo nano -w /etc/crontab

Now, add the following two lines to the end of the file:

0 */12 * * * root letsencrypt renew
5 */12 * * * root unifi_ssl_import.sh

Save and exit nano by doing CTRL+X followed by Y.

The first command renews the certificate every 12 hours on the hour, and the second command re-runs the UniFi script 5 minutes later.

That’s it!



Comments 19

  1. Very helpful. Perhaps my ignorance, but a 12-hour renewal seems a bit “aggressive”. Any reason this isn’t further out like a month or two if the cert is, in fact, good for three months?

    Love your stuff and thanks for all the tips!

    1. Post

      I didn’t mention this in my video (I should have), but this only *checks* every 12 hours. Once the cert is within X number of days of expiring (I forget what X is – maybe 30 days), then it renews. Otherwise, all is well.

      1. I think the debian package creates /etc/cron.d/certbot that autorenews certs. I have not checked on ubuntu, but it might be unnecessary to create a renew crontab. The default for that crontab is also once every 12 hours.

      2. Right…. but the unifi_ssl_import.sh script going to restart your UniFi controller every 12 hours.
        Seems like unnecessary downtime, especially if you are using features that require the controller to be online, like guest portal.

  2. Rather than opening 80 and 443 all the time, you should use this command in your renewal command in crontab. This will open the ports as certbot runs the renewal then automatically close the ports when it finishes.

    certbot renew –pre-hook “ufw allow 80&&ufw allow 443” –post-hook “ufw delete allow 80&&ufw delete allow 443” –quiet

    1. Hi Erik,

      Not sure how to do what you suggested.

      I have installed certbot, do I just add,

      certbot renew –pre-hook “ufw allow 80&&ufw allow 443” –post-hook “ufw delete allow 80&&ufw delete allow 443” –quiet

      to the sudo nano -w /etc/crontab

      thanks in advance

      1. Wondering this myself. More than willing to add, but also have an additional question. If I do a 80 —> 443 redirect, will that affect certbot’s ability to renew?

  3. Any chance for a DNS-01 version of this? My ISP blocks port 80.

    I’m currently doing that manually on a different machine (with godaddy dns), but I’ve never successfully got it to be fully automated. I think I’ve just found a complete walk-through on how to get it going and am not quite skilled/smart enough to manage it.

    But also, I’m going to have to dig through this a few times. I think you’re showing me how to get my certs into the unifi controller with unifi_ssl_import.sh. I have been wanting to do that with the controller and unifi video server as well.

    1. Hi Matteo,

      Is this still an issue? I want to start an installation using the CloudKey on a new implementation and was wondering if this is going to work.

  4. First: Thanks for the script. I know this an a few more but knowing that it is tested was a good thing.

    There seems to be nothing in the script preventing it from just running through everytime. So it will restart your controller twice a day!!
    You should do the following:
    1) Save this to `/usr/local/bin/unifi_post-renew.sh`

    set -e

    for domain in $RENEWED_DOMAINS; do
    case $domain in

    2) Change `0 */12 * * * root letsencrypt renew` to `0 */12 * * * root letsencrypt renew –deploy-hook /usr/local/bin/unifi_post-renew.sh`

    This will only run the script when the cert is actually renewed

    1. Would you still leave the “5 */12 * * * root unifi_ssl_import.sh” in the crontab section?

      It looks like you are wanting to run it from the first part?

      Thanks for the feedback

    2. Adding to the question of Erik, do you need to customise the “unifi.company.com)” part of you script or it refer to something else? Thank you!

  5. Hi, Thanks for the script, all is work good, but and after install the public ssl, I have a problem with guest portal, on the admin page is without problem, but on the guest portal, sometime i get warrning for unsecure, and when i check site on ssl checker https://www.geocerts.com/ssl-checker i get this message.

    “A valid Root CA Certificate could not be located, the certificate will likely display browser warnings”

    Do you know, what is a possible problem ?

    Thank you

  6. Hey! I followed all the steps, but I believe the script is defective. The execution of the script is looped with the = yes statement. Can you verify this? Thank you

Leave a Reply

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