Set up UniFi Controller on Google Cloud Platform

Google offers one free virtual machine on Google Cloud Platform. This script will make setting up a UniFi Controller on GCP a breeze and it includes all the goodies. No command line required, everything is done in the GCP Console and it takes 15 minutes total and that includes transferring your current sites to the cloud. You should try this!

[UPDATE 07/2018] The problem reported by GJ has been solved.

The script I have created will set up an UniFi Controller on GCP with these extras:

  • Automatically acquire and install a Let’s Encrypt certificate to relieve you from HTTPS certificate warnings. For a certificate you need to have a domain name for your controller, dynamic or static.
  • Dynamic DNS support to help you set up the domain name.
  • You don’t need to type https://your.server.dns:8443 every time. You just type the DNS name of your controller and it will be redirected to HTTPS port 8443 automatically.
  • Backup files will be copied to a Google Storage bucket for offline storage.
  • Fail2Ban protects your controller from attackers trying to brute force the password: the IP address of the offender will be denied access for an hour after every three failed attempts.
  • The free tier GCP micro instance comes with only 600MB of memory. The script will create a swap file to accommodate the controller.
  • The underlying Linux system and UniFi Controller will be automatically updated to the latest stable releases.
  • If the MongoDB database supporting UniFi Controller needs repairing, a reboot will do the trick. There is a support script to run the repair commands as needed before the controller starts.
  • HAVEGEd will store entropy for encryption. Without sufficient entropy available rebooting the server may take 30 minutes or more.

None of this will cost a dime to try out. At least you can watch this 10 minute video to see the whole procedure including transferring your current controller:

(There are subtitles available under the YouTube gear icon)

1. Preliminaries

Take a backup of your current controller in Settings > Maintenance. This is always a good idea. You may also want first to improve your password. In Google Cloud your controller can be accessed by anyone who guesses your username and password. You don’t want that. Fail2Ban will lock the IP address for an hour after three failed attempts, but it won’t help if your password is guessable.

A proper SSL certificate requires a DNS name that points to your controller. You can register your own domain or use one of the free ones that dynamic DNS providers offer. If you want to use a dDNS service, you need to set it up first. There are links to some providers at the end. If you control your DNS yourself you won’t need dDNS.

Next you have to create an account on Google Cloud Platform. You will need a credit card for this, but the micro instance that Google offers for free is sufficient for a small UniFi Controller and the free 5GB on Cloud Storage is plenty for the backups. Google does not guarantee this offer will run forever, but it has run for years now and they will certainly let you know if this changes. The current price for a micro instance is $4.28 per month, if you need to run another for example.

First of all, use a good password or better yet, set up two factor authentication for your Google account. If someone breaks into your account the intruder could create a huge bill in just a few hours. You can also set a spending limit in Billing > Budgets & Alerts. Create a new budget for the whole billing account and set a budget. $50 or even $10 should be enough, if you ever want to do some testing on an extra controller for a few hours. The budget feature won’t protect you from an intruder breaking into your account, however. The first thing the intruder would do is to remove the limit.

If you set up budget alerts (50%, 90% and 100% by default) you need to nominate yourself to a billing administrator to receive the alert emails. This is done in IAM & Admin > IAM section. Locate your identity in the list and click in the Roles column. Check Billing > Project Billing Manager to add this role to your identity.

UPDATE: Some readers have reported that they needed to enable the storage API in APIs & Services > Dashboard > Enable APIs & Services > search for Google Cloud Storage JSON API > click Google Cloud Storage JSON API tile > click Enable button. My account had this enabled by default, but my account is older and perhaps the default has been changed. Thank you Limbo and Chrysilis!

2. Create a Storage Bucket (optional)

UniFi Controller will create backups automatically. The default is once a month, but you can change this in UniFi Controller > Settings > Auto Backup. I run backups once a week and keep the last 26 or a half year’s worth. These backups are stored on the computer the controller runs on. If this computer is lost for some reason, so are the backups. You should store them somewhere else and a Google Storage bucket is the perfect place. If you create a bucket the script will sync the backups to the bucket daily.

In GCP Console switch to Storage > Browser and create a new bucket. The bucket name has to be globally unique, so all the obvious ones are already in use. Use something like petri-office-unifi-backup. For the free offer select Regional as the Storage class and choose a Regional Location in the U.S. I am in Europe and East Coast is marginally closer, so I use it.

3. Set Up the Virtual Network

This is something you will need to do only once. All your future UniFi controllers will use the same network settings.

Switch to VPC Network > Firewall Rules. Create a new firewall rule and name it allow-unifi for example, the name isn’t important. In the Target tags field enter unifi. You can use any name for the tag, but you will need to remember it for the next step. In the Source IP range type 0.0.0.0/0 to denote any address. In the Protocols and ports section select Specified protocols and ports and copy this into the field:

tcp:8443;tcp:8080;udp:3478;tcp:8880;tcp:8843;tcp:6789;tcp:443;tcp:80

Switch to  VPC Networks > External IP Addresses and click on Reserve Static Address. You need to give a name for the address, choose the region where your virtual machine is going to run and click Reserve. I suggest you use the same region as the Storage bucket. You can’t attach the address yet, since you haven’t yet created the virtual machine. (Google will charge for the static addresses while they are not in use. The plan is to run the controller non-stop so this is not an issue. Just remember to release the address if you don’t need it any longer.) If you manage your DNS yourself, add an A record for this IP at this point.

4. Create the Virtual Machine

In Compute Engine > VM Instances create a new instance. You can name it anything you like. Select a Zone in the U.S. for the free offer. You need to place the VM in a zone that is in the same region as the static address. The Machine type should be micro for the free instance. By default the Boot disk is 10GB which suffices, but it is tricky to expand afterwards. The free offer is 30GB but I prefer to set the boot disk to 15GB in case I need to run another VM. To change the size, click on Change and set the size. The script is designed to run on Debian Linux so don’t change that. Click Select to save.

If you created a bucket for the backups, you need to allow the virtual machine to write to Google Storage. The default permission is Read Only. In Access Scopes choose Set access for each API. In the list that appears change Storage to Read Write.

No need to select the HTTP and HTTPS options in the Firewall section. You created your own firewall rules already.

Click on Management, disks, networking, SSH keys to open advanced options. On the appearing Management tab add the following Metadata key/value pairs. The first one is mandatory: the script that does the magic. All the others are optional and the order doesn’t matter. There is no way to do any error checking or report script failures, some feature will just not work if the metadata is erroneous. You can come back and change them if necessary. The changes will take effect after next reboot. See the Maintenance section at the end for how to reboot.

Key Value Purpose
startup-script-url gs://petri-unifi/startup.sh Required!
ddns-url http://your.dyn.dns/update.php?key=xxxxx Helps you access the Controller
timezone Europe/Helsinki Lets you adjust the reboot hour
dns-name your.server.dns Required for HTTPS certificate
bucket your-bucket-name-here Required for offline backups

Click on the Networking tab and add unifi to the Network Tags field. This tag ties the firewall rules you created earlier to this virtual machine instance. This step is easy to forget, but missing it will prevent access to your controller! Click on Default under Network Interfaces and choose the static IP address you created earlier as the External IP.

Click Create. Go get coffee. Even if the virtual machine appears to be running, the script is not done yet. It usually takes less than five minutes, but give it ten.

5. Set Up the Controller

Connect to your new controller. On the first screen of the UniFi wizard click restore from a previous backup and upload the latest backup. Wait for the restore to complete. Log on to the new controller using your old username and password. In Settings > Controller add the DNS name or the IP address of the new controller to Controller Hostname/IP field and check Override inform host with controller hostname/IP. Confirm the change. Click Apply Settings.

Connect to your old controller. Do the same change in Settings > Controller and add the DNS name or the IP address to Controller Hostname/IP field and check Override inform host with controller hostname/IP. This setting is for the current devices that are already associated with the current controller. Now they will start to contact the new controller in Google Cloud.

Connect to your new controller again. Check the devices page to see whether the devices come online eventually. If they don’t, you may have to force reprovisions them on the old controller. In that case go to the Devices page on the old controller, select each device in turn and on the Config tab click Provision under Manage Device.

How to Set Up a New Network with a GCP Controller

If you are starting from scratch you won’t have any controller backup to transfer to the GCP based UniFi Controller. In that case you have three alternatives:

  1. Install a temporary controller on a local computer, set up the network and transfer the controller to GCP as outlined above.
  2. If you want to do a pure cloud based installation, you’ll need to tell the devices the address of the controller. If you have an UniFi Security Gateway connect its WAN interface to the Internet and your laptop directly to the LAN interface. Connect to the USG with a browser (https://192.168.1.1) and configure the inform URL to http://your.server.dns:8080/inform. Once you have adopted the USG in the controller other UniFi devices will appear in the controller as you start to connect them.
  3. If you have some other router than an USG, you can either set your DNS server to resolve name unifi to the IP address of your cloud controller or set the DHCP option 43 in your DHCP server. There are examples of both in Ubiquiti’s L3 Adoption Guide. The last resort is to SSH to each device in turn and use command
    set-inform http://your.server.dns:8080/inform.

Maintenance and troubleshooting

If your devices won’t switch to the new controller the reason is usually either in the firewall or DNS. Is your VPC firewall rule correct and does the network tag of the virtual machine match the tag on the rule? Is your DNS name correct both in the metadata and in both controllers’ Override Inform Host fields and does the DNS name point to the correct IP address? In either case the devices will try to connect for several minutes before reconnecting to the old controller. Sometimes it takes hours for DNS to settle, depending on the time-to-live settings. In that case let it sit overnight and force provision the devices next morning.

Let’s Encrypt limits the number of certificates issued per domain to five per week (at the time). This usually isn’t an issue, but if you use the free domains offered by dDNS providers, the most popular domains will have used this week’s five certificates already. In this case the script will try to acquire a certificate once an hour until it succeeds. You will see certificate warnings at first but the address bar should eventually turn green. You can also anticipate this and choose a less popular domain (or register your own).

Don’t set up two controllers to back up to the same bucket. They will delete each other’s backups. You can either create a new bucket or create folders inside the bucket. In the metadata set bucket to your-bucket/foldername.

Google blocks sending email directly to SMTP port 25 from within GCP because they don’t want all spammers on board. If you want to receive email notifications you need to set up authenticated SMTP on port 587 or 465 in UniFi’s Settings > Controller > Mail Server. You can use your personal email account or even use Google’s email engine if you can prove you are not a spammer.

The Cloud Console will typically start displaying a recommendation to increase performance by upgrading to a bigger virtual machine (at cost), even though the CPU utilisation graph typically stays below 15%. Apparently the updates the devices send cause very short spikes, because occasionally the devices report that the controller is busy. This may cause some inaccuracies in the reports, I am not certain. If you can live with that, just dismiss the suggestion. In my experience the micro instance can serve at least the same 30 devices a Cloud Key can. You can also use this script on any virtual machine type you like. When you select the machine type you see the cost per month. You get a better price if you can commit to use the resource for 1 or 3 years.

If the controller appears to be malfunctioning the first remedy is to reboot it. A reboot is also the way to re-run the script if you made changes to the metadata. The safe way to reboot is to Stop and then Start the virtual machine. This won’t change the IP address since you have reserved a static address. Don’t use the Reset button. A Reset will immediately restart the virtual machine, which may damage the UniFi database and/or the Linux filesystem (if you are really unlucky).

The underlying Linux system and UniFi Controller will be automatically updated to the latest stable releases. If an update requires a server restart or a reboot it will occur after 04AM UTC. Set the timezone metadata to adjust the reboot to 04AM local time. A reboot will make a captive wireless guest portal inaccessible for a couple of minutes so you don’t want it to happen during peak hours.

Automatically updating the system is a risk. For example there could be an update to Java or MongoDB that is incompatible with the installed UniFi controller. There is no way you could prevent the installation of the update. However, I decided that NOT updating the system is even bigger risk. I don’t trust the intended audience (you) to log on regularly over SSH and run apt-get. In any case, if the risk realizes I trust that it will be transient. The incompatibility will affect a lot of other users as well and there will be another update to solve the issue. This other update will be also automatically installed. If you are familiar with Linux you can also log on the VM and edit /etc/apt/apt.conf.d/51unattended-upgrades-unifi to your requirements.

The automatic update won’t do major Linux version upgrades. Every couple of years it is wise to upgrade the underlying operating system. The Cloud Way is to create a new UniFi controller:

  1. Take a backup of the old controller.
  2. Create a new bucket (or folder).
  3. Remove the static IP address from the old virtual machine (VPC Network > External IP Addresses and Change to None)
  4. Create a new virtual machine as outlined above and restore the backup
  5. Delete the old virtual machine
  6. Done! Since the IP address is the same there is no need to update any settings and the turnover is immediate.

If your controller starts to act up and rebooting doesn’t help, just create a new one. Just like upgrading the underlying Linux, create a new virtual machine. If you cannot connect to the old controller to retrieve the backup, use the latest auto backup in the storage bucket. That’s what they are for. Don’t fall in love with your virtual machine, you should treat them like pencils: very useful but replaceable.

If you are concerned about the security of the script you better read it through first (the link is at the end). Be aware that the script is in my bucket so I can update it as necessary. I could also make malicious changes that would take effect on your server after next boot (after a system update for instance). If you are cautious you should remove the whole startup-script-url row from the metadata as soon as you are happy with the other metadata settings. The script doesn’t do anything once the system is configured. If you ever need to reconfigure something you can just add the startup-script-url back, Stop and Start the VM to run the script and then remove the row again.

Google currently offers for free one micro instance with 30GB of disk space and 5GB of bucket storage in the U.S. regions. Both come with monthly 1GB of free egress (i.e. outbound) data transfer with China and Australia excluded. Egress data is used when you download backups to your computer and when your devices download upgrades from the controller. The static IP address is free as long as it is attached to a running virtual machine. You will be charged if you exceed these limits, but typically the charges are minuscule. For example when you upgrade your Linux you will run another micro instance for a while. The budget feature should protect you from nasty surprises.

Links

Some dynamic DNS providers and how their single line update URLs look like:

  • Afraid.org
    http://freedns.afraid.org/dynamic/update.php?xxxdynamicTokenxxx
  • DNSPark
    https://username:passsword@control.dnspark.com/api/dynamic/update.php?hostname=home.example.com
  • DuckDNS
    https://www.duckdns.org/update?domains={YOURVALUE}&token={YOURVALUE}
  • DynDNS
    https://{user}:{updater client key}@members.dyndns.org/v3/update?hostname={hostname}
  • EasyDNS
    https://username:dynamictoken@api.cp.easydns.com/dyn/generic.php?hostname=example.com
  • NameCheap
    https://dynamicdns.park-your-domain.com/update?host=[host]&domain=[domain_name]&password=[ddns_password]
  • Sitelutions
    https://dnsup.sitelutions.com/dnsup?id=990331&user=myemail@mydomain.com&pass=SecretPass
  • ZoneEdit
    https://<username>:<password>@dynamic.zoneedit.com/auth/dynamic.html?host=<hostname>

 

91 thoughts on “Set up UniFi Controller on Google Cloud Platform”

  1. The biggest update so far didn’t go cleanly. Upgrading UniFi controller 5.6 to 5.7.20 killed the MongoDB server disabling the controller. Starting and stopping the virtual machine recovers it. I can’t control the update package contents and instructions. I can only hope for the best. Unattended upgrading is always a gamble.

  2. Hi Petri, thank you very much for all the work you shared with us.
    I follow your procedure and the video but controller is not working.
    How can I check if it’s been downloading and rouning?
    Can I force via SSH script execution so I can see if any error appears?

    Thank you again.

    1. You can SSH into the virtual machine from the Google Cloud Console. Click the SSH button and a pop-up window will appear (unless your pop-up blocker gets into way). No password is required, you are already authenticated in the console. The SSH session is useful if you want to see the logs in your VM.

      Another option is to click on the name of the virtual machine in the VM Instances list. On the VM Instance Details page you’ll see a small link for “Serial Port 1 (Console)”. That will show you all the startup messages. The screen doesn’t update automatically if you want to watch it in real time. You need to click the Refresh for an update. The lines containing “startup-script:” are produced from my script. Do you see any errors?

      1. Thanks for your Reply Prtri:
        In “Serial Port 1 (Console)” there are 6 lines with startup-script:
        Apr 15 17:45:03 unifi-controller startup-script: INFO Starting startup scripts.
        Apr 15 17:45:03 unifi-controller startup-script: INFO Found startup-script-url in metadata.
        Apr 15 17:45:03 unifi-controller startup-script: INFO Downloading url from gs://petri-unifi/startup.sh to /startup-Vm93gn/tmp792gVc using gsutil.
        Apr 15 17:45:08 unifi-controller startup-script: WARNING Could not download gs://petri-unifi/startup.sh using gsutil. Command ‘[‘gsutil’, ‘cp’, u’gs://petri-unifi/startup.sh’, ‘/startup-Vm93gn/tmp792gVc’]’ returned non-zero exit status 1.
        Apr 15 17:45:08 unifi-controller startup-script: INFO No startup scripts found in metadata.
        Apr 15 17:45:08 unifi-controller startup-script: INFO Finished running startup scripts.

        I don’t explain myselft correctly in my last comment, I wanted to know how to execute your script manually 😉
        I found how to download it and executed with sudo, it seem my VM is not accesible from itself:
        Script logrotate set up
        MongoDB logrotate set up
        IPv6 disabled
        ERROR: Address 35.190.181.102 has not changed.
        Dynamic DNS accessed
        592 megabytes of memory detected
        Swap file created
        Localtime set to Europe/Madrid
        Unifi added to APT sources
        System upgraded
        Haveged installed
        CertBot installed
        Extracting templates from packages: 100%
        Unifi installed
        Synchronizing state of mongodb.service with SysV service script with /lib/systemd/systemd-sysv-install.
        Executing: /lib/systemd/systemd-sysv-install disable mongodb
        Lighttpd installed
        Fail2Ban installed
        Unattended upgrades set up
        Created symlink /etc/systemd/system/multi-user.target.wants/unifidb-repair.service → /etc/systemd/system/unifidb-re
        pair.service.
        Unifi DB autorepair set up
        Backups to bdlsolucinoes-backup set up
        Saving debug log to /var/log/letsencrypt/letsencrypt.log
        Plugins selected: Authenticator standalone, Installer None
        Registering without email!
        Obtaining a new certificate
        Performing the following challenges:
        http-01 challenge for mysub.domain.com
        Waiting for verification…
        Cleaning up challenges
        Failed authorization procedure. mysub.domain.com (http-01): urn:acme:error:connection :: The server could n
        ot connect to the client to verify the domain :: Fetching http://mysub.domain.com/.well-known/acme-challeng
        e/rKudGtFidPxCAHNxR__YzQ3qb8PohwWs38yaS6voXcg: Timeout

        I pinged “mysub.domain.com” from VM and my PC, always reply public GCP IP configured as you explained.

        From my PC http://mysub.domain.com or /.well-known/acme-challeng shows ERR_CONNECTION_TIMED_OUT

        Maybe webserver is not working?
        Thanks.

        1. The first part of your post indicates that the VM couldn’t load the startup.sh script at all. That’s odd. I don’t have any other GCP accounts to try on right now. I’ll look into this.

          The second part where you run the script manually indicates that Let’s Encrypt service cannot connect to your domain from their data center. Does the dns-name metadata resolve to 35.190.181.102 (check for typos)? Is port 80 included in the firewall rule and the same tag applied to the VM? Testing tools are dig, nslookup, getent or host for the former and fetch or curl for the latter question.

          1. Hi Petri: your script works great! It’s awasome.

            Connection to my instance was failing becouse I forgot /0 in the 0.0.0.0 on firewall source… 😀

            About startup-script: I found this in serial log:
            ——
            Apr 18 15:25:43 unifi-controller google_metadata_script_runner[683]: AccessDeniedException: 403 Access Not Configured. Please go to the Google Cloud Platform Console (https://cloud.google.com/console#/project) for your project, select APIs and Auth and enable the Google Cloud Storage JSON API.
            ——

            I finally fixed it.

            I want to ask you something: is it as difficult as I read to set a port mapping in GCP?
            My current ISP blocks 8080 so I’m running CloudKey Unifi at TCP/8082

            To easily migrate all my deviced, as you wrote, I want to change controller hostname. So I need to redirect GCP-public-IP:8082 -> VM:8080

            I read lots of commands to enable this port mapping. Do you know a simple way?

            Thank you again! An incredible job.

  3. You sir, are the man!

    Awesome work, seems you’ve thought of everything.

    This must be the only GCP+Unifi tutorial to exist, and it’s fairly easy to follow, it was an absolute treasure of a find, especially given that GCP just happens to be the most difficult to figure out..

    This is killer, it works perfectly, many thanks for this, much appreciated!

    1. Thanks! If you did set up your system recently, it proves that the script download problem in the previous comment by Limbo is either new or random occurence.

      1. Hi Petri, thanks for putting this together! I had the same error as Limbo “AccessDeniedException: 403 Access Not Configured. Please go to the Google Cloud Platform Console (https://cloud.google.com/console#/project) for your project, select APIs and Auth and enable the Google Cloud Storage JSON API.” Once I enabled the Google Cloud Storage JSON API and restarted the VM all was well.

        1. Do you mean APIs and Services > Dashboard ? I have never changed anything there and all services are enabled (there is a button to disable each). Could it be that my account is older and the defaults have changed? The only API that has any request history is the Compute Engine API. None for the Storage JSON API in the past 30 days. I have no clue.

          1. APIs & Services > Dashboard > Enable APIs & Services > Search for “Google Cloud Storage JSON API” > Click “Google Cloud Storage JSON API” tile > Click Enable button

            Without Google Cloud Storage JSON API enabled the VM could not access the startup script. Note that like for you on the APIs & Services Dashboard I see no requests against Google Cloud Storage JSON API.

          2. Thank you for reporting this. I added an update to the post in the Preliminaries section.

  4. Hi! Petri:
    I followed your article, I found that certbot doesn’t work for my cloud controller on GCP.

    Message of Serial Port 1 (Console) contain “startup-script:” looked like error info to me:
    Apr 21 20:33:13 unifi-controller startup-script: INFO startup-script-url: debconf: unable to initialize frontend: Dialog
    Apr 21 20:33:13 unifi-controller startup-script: INFO startup-script-url: debconf: (TERM is not set, so the dialog frontend is not usable.)
    Apr 21 20:33:13 unifi-controller startup-script: INFO startup-script-url: debconf: falling back to frontend: Readline
    Apr 21 20:33:13 unifi-controller startup-script: INFO startup-script-url: debconf: unable to initialize frontend: Readline
    Apr 21 20:33:13 unifi-controller startup-script: INFO startup-script-url: debconf: (Can’t locate Term/ReadLine.pm in @INC (you may need to install the Term::ReadLine module) (@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.24.1 /usr/local/share/perl/5.24.1 /usr/lib/x86_64-linux-gnu/perl5/5.24 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.24 /usr/share/perl/5.24 /usr/local/lib/site_perl /usr/lib/x86_64-linux-gnu/perl-base .) at /usr/share/perl5/Debconf/FrontEnd/Readline.pm line 7, line 2.)
    Apr 21 20:33:13 unifi-controller startup-script: INFO startup-script-url: debconf: falling back to frontend: Teletype
    Apr 21 20:33:13 unifi-controller startup-script: INFO startup-script-url: dpkg-preconfigure: unable to re-open stdin:
    Apr 22 04:38:04 unifi-controller startup-script: INFO startup-script-url: /usr/local/sbin/certbotrun.sh: 7: [: =: argument expected

    Then I managed to use acme.sh which is an alternative to certbot to apply let’s encrypt SSL certificate:
    https://www.naschenweng.info/2017/01/06/securing-ubiquiti-unifi-cloud-key-encrypt-automatic-dns-01-challenge/

    The certificate then installed successfully.

    Is there anything I need to do with the error info besides certbot?
    Thanks.

    1. Each and every apt-get install will create all those debconf and dpkg-preconfigure errors to the log. I haven’t found a way to suppress them without losing valuable debug info. Apt-get is just trying to communicate with the user and doesn’t find a controlling terminal.
      The last line is significant, though, about the certbotrun.sh. It means that for some reason the script couldn’t retrieve the external IP address of the VM. Does your VM have one? I couldn’t reproduce this. Anyways, I have updated the script (currently at 1.0.3) so that it will output a more useful error message instead.
      I haven’t looked into acme.sh. You may have to import the certificate manually to the Java keystore after every renewal. Or you can edit /etc/letsencrypt/renewal-hooks/deploy/unifi to work with acme.sh.

      1. Hi~ Petri,
        I’ve reserved a static external IP address for my cloud Controller VM on GCP as your tutorial steps.
        I am fine with acme.sh, just created a crontab to perform the auto renewal.
        Thanks for your reply!

  5. How do I use Let’s Encrypt with this ?

    How do I see what is in the gs script ?

    Can this setup be done for UNMS ?

    1. 1) Create a DNS name and point it to your VM instance. Add the DNS name to the VM metadata with key dns-name as instructed. Everything else is automated.
      2) At the end of the article in section Links there is a link “The startup script on Google Storage”
      3) My script won’t do it, but you could write a similar setup for UNMS. You can also read through the script and do the steps manually to apply them to an UNMS instance.

  6. Hi Petri!
    First of all, great job and thank you for share your work!

    I followed your video, step by step, but, when i connect to VM, the browser (Opera and Chrome) says “Not Secure”, there is any way to solve this issue?

    1. If you click on the lock icon and check the certificate details, is it a UBNT self-signed certificate or a Let’s Encrypt one? To me it sounds like your VM didn’t receive a LE certificate.

      The most probable cause is that the domain-name metadata didn’t resolve to the external IP of the VM. If you reboot the VM by stopping and starting and then click the VM name to see the details there is a link to the Serial port 1 (console) where you can see all the output from the boot process. Only lines containing “startup-script” relate to my script. If you see a line “No action because aaaaa.bbb doesn’t resolve to vv.xx.yy.zz.” then that is the cause.

      It may be easier to just delete the old VM and create a new one. It is the Cloud Way. It could be that a DNS glitch made the LE check fail. If you delete the old one first then it won’t cost you but 10 minutes of work.

  7. Thanks for this tutorial, this is great!

    I’d like to modify the script to install unifi-testing rather than stable. It looks like the only area I would need to modify your script is line 88. Can you confirm? If so, I’ll modify your script to include that change and then host it in my own bucket.

    1. Yes, that’s the only place. You can also just copy and paste the whole script to the startup-script textbox in GCP Console. No need to use a bucket, if you only need one VM.

      1. Thanks! Last question. I have a custom config.gateway.json file for use with CloudFlare DDNS. How would you suggest I add this in a scripted way?

        1. Add a line like (if the file is on a web server):
          curl -Lfs -o /var/lib/unifi/config.gateway.json http://www.some.dns/path/setup.json
          or (if the file is in a bucket):
          gsutil cp gs://bucket/setup.json /var/lib/unifi/config.gateway.json
          Disclaimer: I haven’t tested either one. I just typed them off the top of my head. E.g you may need to adjust the permissions as an extra step.

          1. I don’t do a lot of shell scripting, but I added a section like this to the end of your file:

            ###########################################################
            #
            # Add Cloudflare DDNS settings to default config.gateway.json
            #
            if [ ! -f /var/lib/unifi/sites/default/config.gateway.json]; then
            cat > /var/lib/unifi/sites/default/config.gateway.json <<_EOF
            {
            "service": {
            "dns": {
            "dynamic": {
            "interface": {
            "eth0": {
            "service": {
            "custom-cloudflare": {
            "host-name": [
            "sub.domain.com"
            ],
            "login": "email@gmail.com",
            "options": [
            "zone=domain.com"
            ],
            "password": "API_KEY_GOES_HERE",
            "protocol": "cloudflare",
            "server": "www.cloudflare.com"
            }
            }
            }
            }
            }
            }
            }
            }
            _EOF
            echo "Cloudflare DDNS added to default config.gateway.json";
            fi

          2. Ahem.. You are right. It should go under the site directory. Your script looks fine to me. Testing will tell the truth.

          3. Oh, one more thing. Don’t put it at the end! If there is no dns-name metadata my script will exit on line 342. Put your addition ahead of the Let’s Encrypt stuff on line 336 for example (or any line before that).

  8. Wow, great call. Thanks for all your help on this.

    I’m slowly moving my homelab to the cloud. This has been a fun and useful learning experience.

  9. Great job Petri! It really helpme, I create 2 vcpu + 7.5 gb Memory +30gb storage, i need to manage about 150 unifi AP, there is no problem with “The script will create a swap file to accommodate the controller.”?, Is the script limiting the memory capacity?

    1. No, the script won’t. It will create a swap file if there is less than 2GB memory. You have more so there won’t be any swapping. UniFi defaults however do limit the controller memory to 1GB. If you log on via the SSH button in GCP Console and edit system.properties file you can increase the initial (xms) and maximum (xmx) memory size of the controller. See Controller Tuning

      1. ok, thanks, I will try that, under your experience, how many ap can you manage with 1gb memory(or users)?

        1. I haven’t done any real testing. Dozens anyway. Ubiquiti quotes 30 devices for 1GB Cloud Key, but I don’t know whether it is memory or processor bound. I haven’t seen the load go up noticeably. Using the GUI is sluggish, though, and periodic MongoDB chores do eat up the processor causing some device updates to go unnoticed. If you can live with those you should be fine.

      2. Hello Petri, I add about 65 ap on the google cloud controller, but im experimenting some issues on some ap, some users report wlan unestable, appears and dissapear or some ap’s become heartbeat missed and then disconected, after that conecten again I improve controller to 3 gb memory on system.properties,, do you have any idea? could you help me? (i will pay you for your service)

        1. The WLAN works even without a connection to the controller. The missed heartbeats just mean a gap in history stats at most. Only if you run a guest network with hotspot you need continuous controller connection. There is some other problem with your network.

          I am abroad this week so I can’t help you right now. You should ask on the Ubiquiti wireless forum. There you get prompt replies for free.

          Of course you could use a slightly larger VM for 65 APs.

          1. That is plenty for a hundreds of APs. The controller performance can’t be the issue here. Do you monitor the uplink from your network to the Internet? If the uplink is congested that could cause the symptoms you are seeing. I can’t see how I could help you further. You should post of on the UniFi forums or contact Ubiquiti support.

  10. Thanks for this post Petri, very helpful.

    One question. I assume the ddns-url metadata field is unnecessary if you are manually pointing your hostname to the static IP through your DNS provider?

  11. Petri, this was extremely helpful and easy to setup with your instructions. Thanks so much for your work.

    I’m up and running but I just have one question. Did you do something to redirect requests to your controller via hostname automatically to 8443 for management or do you have to enter https://yourdomain.com:8443 each time you want to access your cloud controller?

    1. The script installs Lighttpd which will direct everything from port 80 to 8443. To use it enter the plain yourdomain.com in your browser, no https at the start or 8443 at the end. Catching all the possible combinations would have complicated the script far too much.

  12. Kiitos paljon!
    Works like a charm, now I don’t have to worry about a power outage ruining my Raspberry PI controller.

  13. What’s the controller upgrade process like once a controller has been up and running via this script?

    1. Unattended upgrades will run every night and keep both the Linux and the UniFi controller up to date. You won’t have to do anything. It won’t do major Linux upgrades however. Debian “Buster” is expected next summer but “Stretch” will be supported for some time to come.

      1. So the UniFi controller will auto update to the latest stable release that’s available each night? Is there anyway to stop this behavior so that the controller is only upgraded on demand?

        1. Yes. This setup was intended for users without any Linux or cloud skills. I decided that it is safer to upgrade everything automatically than to leave unmanaged VMs running on the Internet. For the target audience the Linux command line is a real barrier.

          Linux savvy users can create and tailor their setups as they see fit. You can use my script as a starting point. If you want to revert to the default behavior with only Linux security upgrades being automatically installed you should just remove /etc/apt/apt.conf.d/51unattended-upgrades-unifi. If you want to disable all automation apply command systemctl disable unattended-upgrades.

          1. Thank you, that was very helpful. I don’t mind messing with the Linux CLI at all. Your script just makes the initial setup quite hands off which is nice.

  14. What’s the best way to troubleshoot the autobackups not being sent to the Google storage bucket? It was working when the controller was first setup as I have one file shown in the storage bucket under the autobackup/ directory but none of the subsequent auto backups that are shown in the controller have been copied to the storage bucket.

    1. What does
      sudo /usr/bin/gsutil rsync -r -d /var/lib/unifi/backup gs://your-bucket-name-here
      produce?

      If that works check /etc/systemd/system/unifi-backup.service to see if the bucket name is correct.

      1. I get the following output:

        CommandException: arg (/var/lib/unifi/backup) does not name a directory, bucket, or bucket subdir.

        1. My bad, I didn’t include the sudo. I’ll edit the answer above in case someone else tries it.

          Alright, the rsync works but your VM isn’t authorized. This is progress. Did you allow Read Write access to Storage for the VM?

          Another, later discovery was that some users had to enable the storage API. I added this update to the post:

          UPDATE: Some readers have reported that they needed to enable the storage API in APIs & Services > Dashboard > Enable APIs & Services > search for Google Cloud Storage JSON API > click Google Cloud Storage JSON API tile > click Enable button.

          1. I confirmed the API was already enabled. In Cloud API access scopes I had Storage set to Read only. I set it to Read Write and started the VM backup. I still get the same output as I showed above.

            AccessDeniedException: 403 Insufficient OAuth2 scope to perform this operation.
            Acceptable scopes: https://www.googleapis.com/auth/cloud-platform

          2. Good, you found the cause. You have corrected it, but nothing in the Cloud World happens instantly. Everything is queued and cached. I suggest you wait til tomorrow.

            If it doesn’t work by tomorrow you should spin up another VM with the proper permissions. Don’t work too hard trying to resurrect broken VMs. Creating a fresh one is the Cloud Way. If you want to learn something new you can detach the current virtual disk and attach it to the new VM so you won’t have to install anything. Although running the script is probably easier and faster.

  15. I’m curious if there are some special settings for the Guest Portal access. I noticed today that guests could connect to the guest wifi but they were not automatically redirected to the guest portal. I’ve added the hostname of the controller to the pre-authorization list. Any ideas why they might not be automatically redirected?

    1. None that I know of. Over here in Finland Guest Portals are rarely used, though. The guest portal redirection is layer 3 anyways, so running the controller elsewhere shouldn’t make a difference.

      Check first that your GCP VPC Firewall allows traffic to the guest portal ports 8880 and 8843. You can check it with the browser: http://your.controller.dns:8880 and https://your.controller.dns:8843 both should produce some result.

      What are your settings in UniFi Controller Settings > Guest Policies ? You could also ask this in the UniFi Wireless forum, where there are many more users who could help you.

  16. Thank you! For some reason, i had small problem with java (or maybe with apt). Unifi daemon did not start because java was not found. When entering java –version, system reported openjdk java fine. But for some reason this was not enough for Unifi.

    I had to ssh in and run sudo apt-get install default-jre -y

    Apt wanted to install several packages (like, +30). After everything was installed and rebooted, i was able to log in to Unifi

    1. Was this just recently? With UniFi 5.8.24? I haven’t rolled a new VM with it yet. My script doesn’t install Java by itself. It should get installed as a prerequisite for UniFi Controller. Apparently someone has goofed upstream. Thanks for reporting this, though.

    2. I confirmed this. I will try to get a response from Ubiquiti whether they will fix their package or should I write a workaround. I added an update at the top of the article about the situation.

      1. Wow, quite the response time! Yes, this was this morning. Thanks for all effort you put in, will be big help to everyone who wants install Unifi to Google VM

        1. I decided to solve this by installing JRE8 manually before UniFi Controller. The script is now updated (and some old scruff was polished as well).

  17. I followed these steps but the webpage never seems to load, I just get “This page isn’t working”. I just ran the script this morning and not sure if I’m doing something wrong.. how do I troubleshoot the installation?

    1. This morning there was a red warning at the top of the article about a problem. It was reported yesterday by GJ and I wrote a workaround today. It has been up for an hour now. Your easiest solution would be to delete the VM you created and spin up a new one. You should still have the backup on hand so it won’t take you but 10 minutes.

    2. I do see an error in script..
      unifi-debian unifidb-repair.sh[631]: bash: /var/log/unifi/gcp-unifi.log: Permission Denied

      Should I worry about this? The above seems to be the only error I’m getting.

      1. But no web page? That is something to worry about.
        The permission denied you are seeing is just about logging, nothing to worry about. It is odd though, since the systemd unit is run as root, so something is very wrong there. If the installation is hosed then I still recommend setting up a new one.
        If you still get this with the new VM please add a comment.

  18. Any idea why I am unable to get my controller to enable cloud access? When i attempt to enable the following error is received “There was an error saving the cloud access changes. No cloud license is assigned.”

    I can access the controller directly without any issues so i guess it’s not that big of a deal, however this was never an issue on AWS, RasPi or Windows hosted controllers.

    1. I never figured out. I had it happen on Cloud Keys and other local servers, too. Occasianally disabling and enabling it a couple of times did the trick – or was the trick waiting for a few days in frustration, I never knew. Trying different accounts didn’t help IIRC. After I switched over to remote controllers I no longer bothered with Cloud Access. Now I can manage the network from anywhere with a direct https connection instead of screen sharing magic. Downloading backups works, less broken or stuck connections, less trouble overall. I haven’t missed Cloud Access at all.

      Cloud Access is very useful for a local server, I admit. It’s a luxury to login from home instead of driving to the office to reboot a misbehaving AP. If you want to troubleshoot it you should ask on UniFi forums

    1. For some reason the Ubiquity downloads were unavailable. Testing those links now everything appears to be OK so perhaps it was some transient error? Have you tried again? And I mean deleting that VM and creating a new one today.
      No, you weren’t doing anything wrong and there is nothing you or I could do about Ubiquity downloads repository or GCP network connecting to it.

    2. On a second thought: the NTPd error just before could indicate there is something wrong with the network. The VM did load the startup-script from my bucket, so it is not completely isolated. My script doesn’t set up NTP because Google does it for us. It uses the virtualization host as the NTP server so it should be closer than the Storage bucket. I can’t say. Just give it some more time.

      Perhaps you should switch to some other zone within your region or to another region altogether. I just tested this in us-central1-c and it went smoothly.

    1. You need to set up authenticated SMTP to some server. You can use your ISP’s SMTP or it could even be Google’s. There’s a paragraph on this under Maintenance and troubleshooting.

  19. Thanks, I have this up and running it’s been really useful. Just been working on sending emails, signing up to Sendgrid via GCP appears to offer more than their normal free plan in terms of number of emails you can send and although the GCP instructions talk about using postfix, the Unifi Controller will allow us to specify the port for sending email. So as soon as sendgrid is configured and we can create an API key and use port 2525 which isn’t blocked to send emails. 🙂

  20. Any way to force update to 5.8.28?
    Installed today but got an issue when importing the backup as my PC already has 5.8.28 and the VM now has 5.8.24.

    1. You need to log on to the VM through the Google Cloud Console SSH button. Then download the version of your choice using curl. Install the package via sudo dpkg -i unifi_sysvinit_all.deb

      1. Hi, I’ve rebooted the VM but still running 5.8.24 and it didn’t upgrade to 5.8.28.

        Is that correct – you mentioned the VM would auto upgrade to stable releases so I assume 5.8.28 isn’t considered stable currently.

        1. After a release is promoted from stable candidate to stable it still takes about a week before it is made available in the repository. Most often it appears on a Monday, but that is not a firm rule. When the release is in the repository unattended-upgrade will install it after 04 AM automatically. No reboot is required. See UniFi Controller Releases

          1. Thanks so much for your reply, makes perfect sense to me. I’m in no rush to upgrade, just more wonding to make sure my install is working as it should (auto upgrade etc).

            You’ve done a great job putting all this information together, really appreciate it all.

      2. Thank you! Figured our myself after reading on the forums that it takes one week for the new release to be added to the repo. Used wget and dpkg but the fun thing was that I could do this from my mobile using the Google cloud app which has built in SSH. That was an epic experience 🙂

        Thank you very much for publishing this solution and keeping it updated. It’s working perfect for me now 🙂

    1. You have two alternatives. The easy one is to wait for a few days for the latest release to hit the repos. Unattended-upgrade will install it for you overnight. The other one involves Unix command line and here is a copy of what I answered Balazs Szabo above:
      You need to log on to the VM through the Google Cloud Console SSH button. Then download the version of your choice using curl. Install the package via sudo dpkg -i unifi_sysvinit_all.deb

  21. Petri – you must have put a lot of time and effort into this so thank you very much for that.

    This is the first time I’ve tried Google and your instructions were excellent. I had a controller up and running in no time and I’m pretty impressed by the performance considering it’s free. I’m now using the instance as my test controller having disabled the auto-upgrade job and done a manual upgrade to one of the Beta versions. Working great 🙂

    Thanks again..

    1. I wrote the script and the article because when I suggested people to use cloud based controllers I got two objections: “I don’t know how to use cloud services” and “I don’t know how to use Linux command line”. The first one can be learned in minutes and the second one can be completely circumvented with the script. My idea was that user never needs to log on. Your post reveals that you are not in the intended target group 🙂

      You can use the script as a starting point, though. If you look at GCP Unifi Controller startup script explained you can see what the script does and at the end there is a list of files which were modified during installation. That post is not quite up to date with the current version, but close enough. I have a couple of improvements in the works and then I’ll update it.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.