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!

[2023/02] If you are impatient and want to upgrade your controller to the latest version, see Jonathan’s comment below. If you want to upgrade your Debian as well, see Derek’s comment. (Use the search in your browser!) I am still waiting for Ubiquiti to come up with an automatic upgrade without the command line.

[2022/12] UniFi Network 7.3 will REQUIRE Java 11. I don’t yet know how smooth the transition will be. I will do my best.

[Note 2021/12] If you are getting a certificate error then see Fix GCP UniFi Controller certificate. It is finally solved [with help from Djo and Derek]! Basically you just need to stop and start your VM.

[Note 2021/09] If you are still being charged for egress traffic, check your network tier. Apparently Premium is on the free offer and Standard is not! [Thanks, gman!]

[Note 2020/01] If Google has charged you for egress traffic to some remote areas of the globe, you can stop Lighttpd as a fix. Log in to the VM and type sudo systemctl stop lighttpd and sudo systemctl disable lighttpd. That means you will need to use the full https://my.ctrl.dns:8443 URL, but your browser should already know it by now.

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. With a new account you will get credit for the first year. 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 > Cloud 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 Region as the Location type and choose a 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 Networking > VPC Network > Firewall. 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 IPv4 ranges type to denote any address. In the Protocols and ports section select Specified protocols and ports and copy these into the TCP and UDP fields:

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

Switch to  VPC Networks > External IP Addresses and click on Reserve Static Address. You need to give the address a name, 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 > Compute Engine > VM Instances create a new instance. You can name it anything you like. Choose between Regions us-west1, us-central1 or us-east1 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 E2 series e2-micro for the free instance.

Click on Change for the Boot disk.  The default operating system is Debian  GNU/Linux 10 (Buster), but UniFi Controller is not supported on Buster. Change the version to Debian GNU/Linux 9 (Stretch) [Kudos to James!]. The default size 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. Also change the Boot disk type to Standard persistent disk (not Balanced) to stay on the free tier [Thanks Steve!]  and then you are ready to 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 Identity and API access 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 Networking, disks, security, management, sole-tenancy to open advanced options. In the Networking section 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! Leave the hostname empty. Click on Default under Network Interfaces and choose the static IP address you created earlier as the External IP. Set Network Service Tier to Premium [apparently Premium is on the Free Tier. Thanks, gman!].

In the Management section 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/ 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

Some users have reported problems with the gs: style URL for the script. You can try instead [Thank you Miczita]

Click Create. Go get coffee. Even if the virtual machine appears to be running, the script is not done yet. It used to run in minutes, but give it a half an hour these days.

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.

[UPDATE:] The controller doesn’t log failed login attempts by default any more. Logging is required for Fail2Ban protection. You need to change the logging level of the controller: Settings > Maintenance > Services > Log level and change Mgmt to More and click Apply. [Thank you Gabe!]

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 ( 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 switchover 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.


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

  • DNSPark
  • DynDNS
    https://{user}:{updater client key}{hostname}
  • Dynu [thank you Moses!]
  • EasyDNS
  • NameCheap[host]&domain=[domain_name]&password=[ddns_password]
  • Sitelutions
  • ZoneEdit

1,162 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.

    1. Hi Petri – thank you for this wonderful post, the guide, the script and most of all the follow-up support. I have everything loaded up and have my personal domain with it’s A record pointing to the static/reserved IP address of the VM. Also, I have the dns-name setup with this same value. I am able to reach the Unifi login screen using my domain – however, I’m unable to login. I am using the same username and password as I use locally but no luck logging in.

      During setup, I loaded the backup settings as instructed above. I also don’t see letsencrypt cert for my domain “”. I have tried to STOP and START the VM 3 times.

      Any ideas?

      1. Your first problem won’t go away with starting and stopping the VM. The user credentials are inside the controller database and somehow they are wrong. You write you can still log in to your old controller with the same credentials. In that case I would suggest to delete the VM, create a new one, download a NEW backup and restore it.

        My script will acquire a Let’s Encrypt certificate if:
        – Your metadata includes a FQDN dns-name
        – The dns-name resolves globally to the public IP of the VM
        – Let’s Encrypt servers from California can connect to the IP via http and https
        Apparently one of the conditions is not met. You need to figure out which one and fix it.

        1. The username/password issue was resolved – I had fat fingered something. ugh.

          I have the FQDN setup in the metadata and properly resolving to and I’m able to use all the functions. The IP is properly setup mapping to the VM.

          Do I need to do anything to trigger the LetsEncrypt servers?

          1. I’m sorry, this had slipped my attention. You need to stop/start your VM so it sees your new metadata.

      1. Hi Petri,
        Thanks for all your work.
        I setup my controller in Google Cloud following your instructions, so far the upgrade went good.
        I am stock in and version 5.10.17 is available.
        What do you recommend to Do?

        1. Stop and start the VM to install the latest version of the script. The upgrade should install next night.

          1. Any idea off the top of your head on were I would have messed up, I have been running this for 6 months+ and have been getting charged like 2-6¢ a month the last few months… Its not a big deal im just checking.

            1. You should be able to dig into the billing details to find what Google is charging for. Google’s billing is complicated so the billing breakdown is also deep. Most users find it contains too much information, so the answer is there.
              My guess is that the charges are for data transfers. If you are running the VM in a different zone than the backup bucket then you’d pay for transferring the backup to the bucket, for example. Or if you keep downloading the backups to your local computer then you’d pay for the outbound traffic. Just browsing the controller will stay below the charging threshold. For example I haven’t been charged for transfers yet.

    2. Hi Petri, thank you for an excellent post. Created my first Cloud controller and all seems well.
      I didn’t point/migrated anything to the new cloud controller yet.
      I have only one question, I had a guest portal (voucher based) on my on-premise controller and had my logo appear on vouchers when printing for guests, how and where do I need to place the logo for it to appear again when printing vouchers?

      1. I honestly don’t know, never tried customizing the vouchers. If you have done it for your current setup it should work the same way for the GCP based controller. It is just a Debian system so the UniFi Base directory is /usr/lib/unifi. You can start a SSH session from the GCP console to get access.

  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/ to /startup-Vm93gn/tmp792gVc using gsutil.
        Apr 15 17:45:08 unifi-controller startup-script: WARNING Could not download gs://petri-unifi/ using gsutil. Command ‘[‘gsutil’, ‘cp’, u’gs://petri-unifi/’, ‘/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 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/ → /etc/systemd/system/unifidb-re
        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
        Waiting for verification…
        Cleaning up challenges
        Failed authorization procedure. (http-01): urn:acme:error:connection :: The server could n
        ot connect to the client to verify the domain :: Fetching
        e/rKudGtFidPxCAHNxR__YzQ3qb8PohwWs38yaS6voXcg: Timeout

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

        From my PC or /.well-known/acme-challeng shows ERR_CONNECTION_TIMED_OUT

        Maybe webserver is not working?

        1. The first part of your post indicates that the VM couldn’t load the 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 (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 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 ( 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 ( 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.

            1. 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/ 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/ 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/ 7: [: =: argument expected

    Then I managed to use which is an alternative to certbot to apply let’s encrypt SSL certificate:

    The certificate then installed successfully.

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

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

      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, 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": [
            "login": "",
            "options": [
            "password": "API_KEY_GOES_HERE",
            "protocol": "cloudflare",
            "server": ""
            echo "Cloudflare DDNS added to default config.gateway.json";

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

            2. 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).

            3. I too have cloudflare…and I doubt my Ip will be changing … I’d like to get this setup. Having said that, I am a little unclear on how to add this to my VM….can anyone help?

              1. So, I think I figured out where to add JC Connell’s Cloudflare script. I created it this way, because I like Petri’s script and it works perfectly, the only thing I wanted to change was the Cloudflare update. So, if anyone can foresee an issues…please let me know. My concern is at this point, is that GCP will pull Petri’s script and execute it, then my Cloudflare DNS update will happen. So, I need ot make sure the timing is correct.

                In my VM instance, I created a custom meta data called: startup-script. and I added these content to the text box:

                #! /bin/sh
                # 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": [
                "login": "",
                "options": [
                "password": "GLOBAL_API_KEY_GOES_HERE",
                "protocol": "cloudflare",
                "server": ""
                echo "Cloudflare DDNS added to default config.gateway.json";

                1. I ran the startup script test using the command:
                  sudo google_metadata_script_runner –script-type startup –debug
                  And the script was throwing an error on line 6 (if [ ! -f /var/lib/unifi/sites/default/config.gateway.json]; then)

                  So, I just removed that line and the script was happy and it created the /var/lib/unifi/sites/default/config.gateway.json file with the correct contents.

                  However, its seems like my setup is not executing the /var/lib/unifi/sites/default/config.gateway.json file…can’t find any errors as to why… I can check cloudflare and the IP has not been updated.

                  Any ideas?

                  1. IIRC you need to Force Provision the USG for it to pick up the config.gateway.json.

                    1. Thanks Petri!
                      I found this article, and was able to text the JSN file that was created, but with the website mentioned in the article and with the CLI. Both tested perfect.

                      However, I don’t have a USG…just a couple of APs right now. So, I am unable to follow the rest of the article about Force Provisioning the USG. 🙁

                    2. Then you are out of luck, I’m afraid. The config.GATEWAY.json refers to the usG. It is additional configuration for the USG not available in the GUI.

  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 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,, 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 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 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.

          2. Petri,
            can’t thank you enough for this easy setup. Based on your comment here, if I delete /etc/apt/apt.conf.d/51unattended-upgrades-unifi via SSH, I could update the Unifi controller to a version of my choosing and still enjoy your automated linux updates and other fixes applied during the nightly scheduled call?

            Thanks again for this work,

  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

      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. For me, the solution to the problem was to prepend the daily executed command with an absolute path to `sudo`.

          So this
          `ExecStart=/usr/bin/gsutil rsync -r -d /var/lib/unifi/backup gs://NAME-OF-THE-BUCKET`
          becomes this
          `ExecStart=/usr/bin/sudo /usr/bin/gsutil rsync -r -d /var/lib/unifi/backup gs://NAME-OF-THE-BUCKET`

          1. Puzzling, since systemd runs the services as root. There shouldn’t be any difference in using sudo. There shouldn’t even be any other user accounts on the VM except for root. Makes no sense to me.
            If this were more widespread there would be many more complaints, so I have to assume there is something fishy in your setup.

            1. So this is an issue after all. I just haven’t been able to reproduce it. I’ll add sudo to the script, it won’t do any harm to anyone. It is just me trying to keep privileges as low as possible, an old (but good) habit. Thank you for reporting!

      2. I get the following when I run that command with sudo:

        Building synchronization state…
        Starting synchronization…
        Removing gs://ovretail-unifi-backup/5.7.23.unf
        AccessDeniedException: 403 Insufficient OAuth2 scope to perform this operation.
        Acceptable scopes:

        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:

            1. 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.

      3. What is the best way to check the bucket name through the browser of the VM instance? Thanks

        1. In Compute Engine console click on the VM name and scroll down to Custom metadata.

          1. Thank you for the fast reply. My bucket name looks right there. If I run the script manually it syncs with the bucket but otherwise it doesn’t appear to be. I know there’s a config also somewhere in the unifi files. I can’t remember how or where to check this.

            1. If you SSH into the VM, then you can look at /etc/systemd/system/unifi-backup.service and /etc/systemd/system/unifi-backup.timer

              1. I couldn’t find any issues with the bucket name anywhere. So I created a whole new vm and bucket and carefully followed the directions making sure access scopes was set to read / write, etc. However I’m still not getting a bucket sync. Any help would be appreciated.

                1. Can you see the backups in /var/lib/unifi/backup ?
                  What happens if you issue command sudo /usr/bin/gsutil rsync -r -d /var/lib/unifi/backup gs://your-bucket-name-here ? Any error message?
                  IIRC someone once had issues with the bucket and it was solved by using the https url for the bucket in /etc/systemd/system/unifi-backup.service.

                  1. No errors. And the command goes through with no hitches. Verified with the bucket and it synced up. Kinda strange because seems like all permissions are in place it’s just not actually doing this automatically.

                    I’ll try adding the whole url, and will see what happens!

                    1. Still no luck tried the https url and it looks like that won’t even run. Even tried someone else’s suggestion to make a direct path to sudo. In the mean time I’ve just made it my habit to ssh into the VM and manually run the command to sync it. It’s odd.

                    2. You are systematically doing something differently from all of us others, but I can’t tell what it is.
                      You don’t need to sync it by hand. You can download the backup through the web user interface and it is the same thing. Unless you are worried about losing log data, it suffices if you download a backup after configuration changes.

                    3. 100% percent sure it’s just something I’m doing or done. No it’s nothing that specific I just sync it because I am a back-up junkie just having access to the latest backups always makes me feel better. So I think I’ll just stick to making a habit of downloading one manually after a big change once I’m all finished with everything.

      4. Hi Petri,

        I also have troubles with the offline backups to the GCP bucket.
        I doublechecked everything: bucket name, meta data, API storage rights set to RW.
        The only way to get the backups to the bucket is by issuing the “gsutil rsync” command you provided above.
        PS: I’ve setup the backup frequency to 1 hour to be able to test.

        This is the ouput I get from “/etc/systemd/system/unifi-backup.service”

        cat /etc/systemd/system/unifi-backup.service
        Description=Daily backup to eerdekens-unifi-backup service

        Any help is greatly appreciated!

        1. Replying to myself: it’s fixed now, my bad. I entered “Brussels” instead of “Europe/Brussels” as timezone in the metadata field.
          I assume this prevented the daily rsync script from running.

          1. Hmm.. The time zone shouldn’t have anything to do with the rsync task. I’ll look into this whenever I have some cycles to spare. I haven’t actually tried using invalid timezone values. I have crowdsourced trying out all imaginable errors to my helpful assistants around the internet ?

  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[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.

  19. How would you setup a email notification In Unifi hosted on GCP for down device’s?

    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.

  20. 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. 🙂

    1. +1 for me, signed up for Sendgrid and used / 465 / SSL with the auth credentials that Sendgrid prompted me to use from my control panel in the SMTP server config of the UniFi controller. No need for the bits in the Sendgrid guide on the GCP marketplace for Postfix.

  21. 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 🙂

  22. Petri, what if i need to upgrade the controller software? How do i proceed?

    Thank you!

    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

  23. 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.

      1. No. You may want to look what I answered Rob on 2018/11/21. Rob wanted to install a legacy version. You can do the same to install a beta version. There is no way to auto-update the beta, though. You need to download the new, remove the old and install the new package of your choice.

  24. First thing is thank you for all your work with this script and tutorial!

    Next thing is I wonder how the new billing setup on google cloud will affect the tutorial and then free teir.

    From the email.
    “Hello Google Compute Engine Customer,

    We are writing to let you know that on October 1, 2018, Google Compute Engine instances with predefined machine types will be billed by their individual resource types (i.e., vCPU, Memory) instead of by their machine type (e.g., n1-standard-2).

    What do I need to know?
    In order to provide additional savings and simplicity, starting October 1, 2018, predefined machine types will be reported as individual vCPUs and memory SKUs. This means that you will receive sustained use discounts for your instances based on your use of the individual vCPUs and GB of memory instead of being limited to instances of the same machine type.

    What do I need to do?
    The changes will take effect automatically and will result in either the same or lower effective bill. No action is required on your part.”

    1. I don’t KNOW, but if you trust Google then the last paragraph says the charges won’t increase. Since zero charge can’t decrease either we’ll have to assume it will stay free.
      I hadn’t seen that notice, thanks for the heads-up. [Edit: I received the same email a couple of hours later.]

  25. I followed the instructions, but the webpage won’t load. It just says Unable to Connect. I can ping the IP address, but I cannot access 8843 through the browser. I made sure all the ports were allowed in the firewall rules, but it acts like it basically ignores them all. If I use the Open Port Check Tool, all the ports are closed. #Frustrated!

    Can anyone help me please? Thank you.

    1. Can you access port 80? It should redirect to port 8443 immediately but you can see the redirect in your browsers developer console. Lighttpd is listening on 80 and doing the redirect. Another debugging option is to look at Serial Port 1 (console) to see the output. The link is available on the VM details page.

      Usually it is easier to delete the VM and start over. You have made a small error at some point. Hopefully you won’t make the same error twice. On popular omission is to not assign the correct network tag to the VM. The network tag ties the firewall rules and the VM together and it would appear as you describe. Watch the video once more and make notes of the steps.

    2. Hi. Thanks for your article. However when I created it it says “Bad Request
      This combination of host and port requires TLS.”?

  26. I already have my controller running on GCP. But I haven’t been successful in getting a cert applied. Do you have a version that checks to see if a stage is already done and skips those steps? I don’t want to mess up my install. Thank you!

    1. My script checks for most of the steps so it won’t retry them on every boot. It can’t detect what you have done manually since there are so many ways to skin a cat. I didn’t intend my script to be run on top of a manual installation so I don’t recommend it. If the cert is the only problem why not try this script by Steve Jenkins. It does pretty much the same as mine in a single-shot fashion.

  27. Thank you so much for pulling this together Petri, I’m sick of having cloud keys failing so was thrilled to find this tutorial and amazed to have a controller running 45mins after signing up for a GCP account. You’re a legend!

      1. Thanks! Although I have one small problem; I made a typo when entering the value for my storage container bucket so backups aren’t transferring. Is there any way to correct this (eg editing the VM instance) or do i need to set up a new instance and run the script again?

        1. You can just edit the metadata and reboot by stopping and starting the VM.

  28. Hello Petri!
    Thank you very much for your hard work!
    A couple of questions: can I use the LTS version of the controller? Will your automatic update script override it with latest stable?
    And am I obliged to use DDNS? If Google gives you a static IP can’t I just use that? Or is it needed for Let’s Encrypt?


    1. You need to edit the script. After you have edited it to install the LTS version you can either save it in your own bucket or copy-paste it to the startup-script meta-data field in the GCP console. Unattended-upgrades will keep all installed packages up to date. (I have never figured out how to install LTS via apt-get.)

      Another untested solution is to run the script as it is. Then log on and uninstall unifi package and manually install your choice. To prevent unattended upgrades from updating the controller you remove line "c=ubiquiti"; from /etc/apt/apt.conf.d/51unattended-upgrades-unifi

      No. You only need to fill in the meta-data fields you want. If you don’t add a bucket your backups won’t be copied. Without timezone GMT is used. If you have your own DNS records you don’t need ddns. If you don’t include dns-name you won’t get a certificate. Certificates can only be issued to DNS names, not to bare IP addresses.

  29. If anyone is confused on the dynamic dns portion, use the reserved static IP address (reserved in step 3) as your destination address. This is the name on The name on other sites may vary. Continue as Petri shows (in his video) using the direct URL in the value field of the ddns-url key and the subdomain.domain in the value field of the dns-name key field.

  30. Dear Petri,
    We want to use certbot .. after using your script.
    I did not use the ddns services ..
    What will be the root map for the certification of the ssl .. (var/www/html) .. in your script we have no ‘home-dir’ as like /srv/htdocs/[domainname]
    BTW .. the main page is still running with ssl error, nice script and video !

    1. I’m afraid I don’t get what you are referring to with “root map for certification”. Current Let’s Encrypt certificates are in /etc/letsencrypt/live/yourdomain/ (actually there are symlinks to the certs). The script uses certbot’s –standalone plugin which will start its own web server for validation. That’s why I have to shut down Lighttpd for renewals.

      What kind of SSL error you are getting? I’ve never seen one or I would have fixed it 🙂 I occasionally check the setup with SSL Labs test which comes out clean. (I have OpenSSL 1.0 which doesn’t include TLS 1.3 so it gives me a B rating. An upgrade is on my to-do list…)

      1. The next error was coming up after the command:
        #certbot certonly –webroot -w /var/www/html/ -d wifi[domainName]

        Failed authorization procedure. wifi.[domainName] (http-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Fetching https://wifi.[domainName]:8443: Invalid port in redirect target.
        Only ports 80 and 443 are supported, not 8443

        I will change 8443 to 443 for generating the key.

        1. This article is about the script I wrote. I am trying to help users who have problems with it. If you want to do it on your own you should read at least.

          If you need a script to fetch the cert then take a look at Steve Jenkin’s script. It is quite close to mine but will only fetch the initial certificate. You need to set up the update cycle and import the renewed cert to the Java key store.

          1. Dear Petri,

            At this moment I had made the correct ssl for port 443.
            When I check this on the url name: https://wifi.[domain_name]/manage/account/login?redirect=%2Fmanage
            But of course I get the error that the page was not forewarded correct.
            It has to go to port 8443.
            What will in combination with the lighttpd webserver the best way to do.
            A standard redirect to port 8443 .. will give the situation that the site is not secured.
            ssl cert. generation for port 8443 is not allowed .. can you explain how you fixed this with the ‘ddns solution’ in your script.
            From their I can hopefully make from their to the correct forwarding from port 443 to 8443, what unifi needs.

            1. Redirections won’t help with a cert error. My guess is that you haven’t imported the cert to the Java keystore as per Steve Jenkin’s script.

              If you need help with Lighttpd configuration and redirects then you should look at Lighttpd documentation, forums and mailing lists.

    1. Yes, it can be done, but my script is GCP specific. It would be quite easy to adapt: how to access the metadata and the rsync command are the only specifics that come to my mind.

      1. Let me first say, thanks for the GCP script.
        It was super, super easy to setup!!

        Is making a Vultr script on your bucket list?

        1. No, I am not going to expand this. I wrote my script as a proof of concept that you actually can install UniFi controller in a cloud VM without any Linux skills. Many part time admins are not comfortable with remote VMs or Linux command line. I chose GCP because you can run the controller there for free. Anyone can fork my script from GitHub and modify it for Azure, AWS or whatever they desire. You could do it for Vultr! The only CGP specific parts are accessing the metadata about the VM (very similar in other system) and rsyncing the backups to a storage bucket (simple as well).

  31. Thanks so much for the script and this amazing guide!

    One question: does the unifi controller software updates by itself? Or do i have to use apt update to get the latest version?


    1. Yes, it will. Not at once though, but after Ubiquiti uploads the latest release to the repositories. It is typically about a week from the announcement. The idea is that the user never needs to log in to the VM. Everything is done within UniFi Controller or GCP Console.

  32. Is there no risk to leaving all these ports open to all ip addresses? I am working on a script that can limit what devices can talk to the server via iptables and specific ports that also auto updates iptables when the dynamic ip changes.

    Also has built in functionality to allow additional ports to be opened up for your dynamic “admin” ips.

    1. Yes, there are risks, but reasonable in my opinion. When a controller adopts devices they exchange credentials, which are used for future device to device communication. The biggest risk lies in the management interface where the user can choose the password. That’s why I stressed the importance of a good password. I also added Fail2Ban to the mix to make password guessing harder. Only the necessary ports are open in the GCP firewall. For example SSH is not and you would also need to take extra steps to allow remote logins to the VM.

      Typically the UniFi devices are behind a NAT device with a dynamic IP. Trying to track this automatically would introduce more points of failure with questionable benefits (since the devices are strongly authenticated). Limiting the IPs where the user can reach the management interface is even more tricky. Most probably the users would be locked out without skills to open the lock by logging to the VM and editing IPTables manually.

      You always need to choose a sweet spot between security and usability. I targeted my script to average, small scale users, with limited technical skills. Yes, you can harden your controller, but it will require more maintenance. If you require high security you should keep the server in your own data center. If you have satellite offices connect them by static VPNs so all connections are controlled and secure. For remote administration you should set up dynamic certificate-based VPN to the controller.

      1. Well said! Thank you for this script btw.. it works awesome! I wasn’t aware of how strong the security was between the devices and the controller.

        1. Do not use the pastebin from above as it will leave you unable to access your server on port 22.

          I have update the below script to take into account how Google Cloud handles SSH access. This script allows you to block all access to Google Cloud Compute Unifi Controller except by the dynamic dns names defined by you in the script. It will run as often as you want via crontab and polls the dynamic IP address and updates the firewall on the Unifi Controller if a new IP address is detected.

  33. Hi,

    I just restarted the vm but in the console i see this:
    unifi-controller login: Sep 28 00:53:41 unifi-controller google_metadata_script_runner[778]: AccessDeniedException: 403 does not have storage.objects.list access to petri-unifi.
    Sep 28 00:53:41 unifi-controller startup-script: WARNING Could not download gs://petri-unifi/ using gsutil. Command ‘[‘gsutil’, ‘cp’, u’gs://petri-unifi/’, ‘/startup-jZMNIO/tmpJaC8dm’]’ returned non-zero exit status 1.
    Sep 28 00:53:41 unifi-controller startup-script: INFO No startup scripts found in metadata.
    Sep 28 00:53:41 unifi-controller startup-script: INFO Finished running startup scripts.

    Can i just ignore this? Or do i have to restart again?

    1. It is my fault, I’m sorry. I did receive some e-mails from Google saying I need to upgrade, but I couldn’t find any hint in the GCP console about this upgrade. So I decided to ignore the e-mails. Apparently they were for real. Now I have “upgraded” my account and everything should be smooth again.

      This incident didn’t affect any controllers already running. The running controllers don’t need the script after the first boot unless you change the metadata. You can even remove the startup-script-url row from the metadata. You just couldn’t create new controllers while the script was not available. If you had your controller already set up you can just ignore the error. Or you can restart to verify the problem is gone, if you want to help.

  34. Awesome work but for the life of me I couldn’t connect. Console log shows:

    Oct 4 01:13:37 wiz-unifi-controller startup-script: INFO Starting startup scripts.
    Oct 4 01:13:37 wiz-unifi-controller startup-script: INFO Found startup-script in metadata.
    Oct 4 01:13:37 wiz-unifi-controller startup-script: INFO startup-script: /startup-J506OP/tmpdAqmV6: line 1: gs://petri-unifi/ No such file or directory
    Oct 4 01:13:37 wiz-unifi-controller startup-script: INFO startup-script: Return code 127.
    Oct 4 01:13:37 wiz-unifi-controller startup-script: INFO Finished running startup scripts.

    Which I assumes means I have a lovely Debian VM without your script running

    1. I’m afraid your assumption is correct. Although the script wouldn’t continue running, it would only install what was required and quit. In your case it didn’t run at all.

      I couldn’t reproduce this. I tried with my own account, but then it could be some obscure permissions issue. I got a friend to spin one up on her account and it succeeded as well. Could you please delete the VM and try again? Please be careful with extra characters that may be produced by copy-paste. I can’t see any on the third line of your console output, but it might be the cause. Or Google had a hiccup on their system. As you can read from the comments, many have succeeded with the first try. I believe even more don’t report it in a comment.

        1. Works for me™ Your mileage may vary.. Both upgrading an existing controller and creating a new one. Usually new Controller releases aren’t the problem. The problem lies in the compatibility with prerequisite software. If only Ubiquiti would make their software compatible with current Java and MongoDB versions.

    2. Replying to an extremely old comment here for the sake of anyone Google-ing this error—the startup script only worked for me on us-east1 region when I set the startup-script-url metadata to the actual https:// endpoint of the script instead of the gs:// endpoint. The equivalent to gs://petri-unifi/ in Google’s HTTP Storage API is

  35. OK, so what needs to be done to allow public key SSH? 🙂

    I’m able to use the SSH button in the cloud console, and I can use ‘gcloud conpute ssh [unifi-instance-name]’. But when I try to ssh from a host a home, it fails. I’ve allowed port 22 on the VCP Network Firewall rules. And I can see that the traffic that matches that rule is allowed and passes to the host.

    Is there anything else in the VM that needs to be changed to allow SSH? (I’ve added my keys in the instance already).

    Thanks and awesome job on this!!

    1. This isn’t about UniFi Controller at all but GCP VM administration. I wrote the script so people would never need to log on to the VM.

      GCP creates a new, temporary user account when you log on through the Console. When you log out the account is removed with the keys you installed. You need to create a new, static user with adduser and install the keys for that user. If you want to use sudo for the new user, you need to create a new file in /etc/sudoers.d with
      username ALL=(ALL:ALL) NOPASSWD:ALL.

  36. Petri, thank you for sharing this with the community, it’s absolutely brilliant.

    I’m all up and running although as I don’t have a domain name to use at the moment I left the SSL config key out. I’ll probably just access the controller via so can you think of any reason to apply a DNS name / SSL cert (incase there’s something I’m missing).

    Also can you see any issues with turning auto-backups to weekly in the UniFi controller?

    Thank you again for sharing!

    1. You can use it without a domain name. You can get a domain name for free, though. I personally had far too much trouble with WebRTC that the cloud service is using. I find it easier to use the controller directly over https. Keep your password long and complex. Anyone can try to guess it over http.

      Your free bucket allowance is 5GB and there is about the same space available on the VM. As long as you don’t fill either one (they are synced) with the backups you should be good to go. I like to keep 26 weekly backups (or 6 months worth) but they are quite small. The network is small so there isn’t that much statistics to gather.

      1. I found an old domain name I had for testing, added the key for the certificate, shutdown and restarted the VM and I’m in business, SSL all added – thank you again!

  37. Thanks!

    Notes: In my case, on Firewall create, there are separate fields for TCP and UDP, had to paste the port list to each separately (tcp: “8443,8080,8880,8843,6789,443,80,22” and udp: “3478”.

    Oddly enough, when editing the same rule, there is only one field: “Specified ports”, containing “tcp:8443,8080,8880,8843,6789,443,80,22; udp:3478” 🙂

    other than that it seems to be working very well. How much can I scale this (if I pay)? Should I set an administrative limit every such-and-such many sites/devices/clients?

    1. The user interface has changed – thanks for the heads up! I’ll update the instructions, but I am not going to shoot the whole video again. Google keeps making these small changes and they never announce them.

      There is very little information on scaling. Take a look at UniFi – Server Hardware & Database Management. It basically says that you should keep monitoring the CPU & RAM usage. They are mostly affected by the number of APs and other network devices unless you run a captive portal as well. In that case the number of client devices starts to matter as well. The good thing about cloud computing is that you can easily move to a bigger instance as needed. You don’t have to buy spare capacity that you expect to need, you can always use exactly the optimal size. Oh, if you are going to pay for the service use the nearest Google data center to keep the latency down.

  38. Hi, I’m new to GCP and of using your script. A couple of questions:
    1. Do I run your script from the GCP command line? (I’m on a Mac and had a bunch of “command not found” when trying to run the script in Terminal.

    2. Do I setup the DDNS after the controller has been created by the script? (How do I know what the IP address of the VM is otherwise?)

    Thanks for all your work on this.

    1. 1) You add a startup-script-url metadata field in the virtual machine config. That way the the VM will run the script automatically at boot. Watch the video one more time.
      2) If you want to set the DDNS statically then you can set it up when you have reserved the IP address. You don’t need to add the dns metadata field in that case.

  39. I’m getting some possible errors when running the script. In particular, a few “is configured multiple times” and “systemctl not found”. I’m running the script from my home directory. I started it, then cancelled to run as root. Screenshot is on Dropbox and linked.

    1. You are apparently running the script manually on your local machine. My script is intended to be run automatically at boot on the remote virtual machine. That’s why it doesn’t work. Did you watch the video one more time as I suggested? There is no command line involved.

  40. I haven’t rewatched the video yet. I promise I will. I setup my VM following the command line info posted on (he’s how I found your web page). When I follow his last command,
    gcloud compute instances create unifi-controller \
    –machine-type f1-micro \
    –image-family ubuntu-1604-lts \
    –image-project ubuntu-os-cloud \
    –boot-disk-type pd-standard \
    –boot-disk-size 25GB \
    –zone us-central1-c \
    –description “Unifi Controller” \
    –scopes=default,storage-rw \
    –tags unifi-server \
    –address unifi-external \

    it completes at command line without error. However, when I login to the new VM (I setup the DDNS and it works) the certificate didn’t load (he says the same thing on a question I posted on that page). Any idea what I might do to get the cert to load correctly? Also, does the script run every time the VM is called from a web browser? It’s OK if it does I just want to know for knowledge sake.

    1. You are asking me to help with MallocArray’s instructions? I haven’t ever tried them. I trust him though, but you should ask him for help.

      My script creates a log at /var/log/unifi/gcp-unifi.log Any hints there?

      Does the DDNS name resolve from any computer on the Internet? (Try from work, library, your cell phone using mobile data, neighbor’s..) It must resolve from Let’s Encrypt’s servers in some data center.

      The script runs on every boot in case you change some of the metadata. With no changes it won’t do much anything. You can remove the startup-script-url from the metadata if you don’t want to run it every time. (I trust you did change the metadata on the last line of the command: dns-name and bucket, didn’t you?)

      1. Hi Petri,

        No, not asking for help with MallocArray’s instructions. I worked through his command line stuff pretty easily. Just wanted to show you his command automatically calls your script. (I watched the section of your video a couple times to make sure the metadata was exactly the same. It was.)

        I can run the VM from every Mac and iOS device. So, that works. The DDNS setting is operational. I’ll have a look at the log, and post back if I find some useful info. Thank you so very much. You are appreciated!!

        1. The log only lists this:
          CertBot run on Mon Nov 5 16:54:48 CST 2018
          Certificate files missing

          This would have been about the time I finished with everything on 11/5/2018.

          1. I just created a new VM using my instructions. It got a certificate just fine. So we can sum this up: there are two sets of instructions. One that works (mine) and one that appears to have problems. Is there some reason why you won’t try my instructions? It will only take you ten minutes to create a new VM. You can even transfer the static IP to the new VM so you don’t need to tinker with DDNS.

            I also noted that MallocArrays’s instructions are for Ubuntu 16.04 LTS. I guess you are using Ubuntu, too. I have only tested mine on Debian. Ubuntu is based on Debian, but they do differ. I can only support you if you follow my instructions to the T (or you have some really exceptionally good reason not to).

    2. Watched the video again. I’m reasonably certain I’ve got everything configured properly. My outstanding problem seems to be the certificate. Please have a look at the web page I listed above. You’ll see the fellow who admins that page said he had deployed a new VM in the last couple of weeks and the certificate wasn’t working for him, either.

      1. Hi Petri,

        I’ve been in IT for quite a few years, and command line is not something I’m shy about. The only reason I haven’t followed your instructions to a T is because I was confused about what the script does & doesn’t do. (For whatever reason, I did not realize I had to start with 1. Preliminaries. I was under the impression the script did everything for me. My bad.) Am I correct that I have to follow the step-by-step as outlined in both the video and the verbal post here (beginning with preliminaries)?

        I was also going to try adding CertBot to my existing VM but wasn’t sure of the choice for I’m using (Apache, Nginx, Haproxy, Plesk or None of the above).

        The Ubuntu VM I created uses 25GB, so I’m thinking I’d have to destroy that or register a new Google Cloud account in order to use your instructions. Does that seem correct?

        1. I made the video to give you the big picture and help decide if you will be able to make it through. Creating a virtual Linux machine in some cloud service sounds intimidating if you have never done it. When actually doing the steps it is easier to have them written down instead of pausing the video. Essentially they are the same thing, but there is more detail in the text.

          If you just created your GCP account then you should have received a couple of hundred dollars worth of credit. You can run several VMs side by side for the money. Even if you don’t, it won’t cost you but a few dollars. You can only attach the IP to one VM at a time, though.

          1. Ultimately, I stopped the Ubuntu VM, changed the static IP it was using to None, then deleted the VM. I followed your directions. The only problem was when I got to the Networking section, it didn’t see the original reserved static IP. I released that, created a new one and attached that to the new VM. After I created the new VM and having changed the DDNS IP, I connected to the new VM & YES, a certificate was attached. Thanks! Now, I’m trying to figure out how to get the APs which are still connected to the proper sites in the VM but show as Disconnected probably because they were setup and adopted on the local Unifi Controller. I’m guessing until I’m back onsite with those APs I won’t be able to get them on the new cloud controller.

            1. You followed instructions and succeeded. Good! There might be a lesson in there 🙂

              Did you also go through step 5 to the end? You should first set the Controller Hostname/IP on the GCP controller and then on the old controller. That’s what will make the devices connect to the new controller.

              You probably had the IP in a different region. Static IPs are tied to a region, you can’t move them. Makes perfect sense if you think about it from the routing perspective.

  41. There’s no question there was a lesson in there and the main reason I went thru all of this was to learn. Success is a great motivator!

    I would like to make one suggestion. I think a preamble explaining to very newbies such as myself, that one HAS to go through the steps to make this all work is important. I never think I’m so unique I’m the first to have the ideas or questions I have. So an explanation that the steps need to be used and THEN the script kicks in at the end would have helped me. If this is the very first time someone didn’t understand (me) I will chalk it up to a new experience.

    Thank you for a great tutorial, but just as grateful you are willing to respond to help anyone who asks questions. So, I have one more: In the reply process, I give my email. Is there anyway to get an alert via that email you have responded? Another of my naive expectations. I thought I’d get an email after your responses, but those didn’t come. I didn’t see your first response til the early afternoon yesterday. Regardless, you are a prince! Very big thank yous for teaching me something new!!

    1. Thanks for your input, but I try to minimize edits to old posts. Let’s see if I get more complaints. I also feel I would belittle readers, if I wrote a cookbook and started by explaining that you really need to take all the steps. You can’t just jump to the end and expect to find a roasted turkey in your oven! 😛

      This site is built around WordPress. WordPress used to notify commenters, but apparently the function has been removed. The email address is not verified by any means so it would be very easy to bully someone by generating thousands of comments around the Internet with the victim’s email. It is sad the Internet has become the environment it is today. I have been using the Internet since the early 80’s, when you really could email any textbook author or a professor in any top university and expect a reply.

  42. I’m having a tough time getting this to work. I got it to work initially once, but then I deleted the VM and started over. Since then, just can’t get anything working anymore.

    Looking at the console, here is what I find.

    Nov 12 06:00:12 unifi-controller startup-script: INFO startup-script-url: debconf: unable to initialize frontend: Dialog

    Nov 12 06:00:12 unifi-controller startup-script: INFO startup-script-url: debconf: (TERM is not set, so the dialog frontend is not usable.)

    Nov 12 06:00:12 unifi-controller startup-script: INFO startup-script-url: debconf: falling back to frontend: Readline

    Nov 12 06:00:12 unifi-controller startup-script: INFO startup-script-url: debconf: unable to initialize frontend: Readline

    Nov 12 06:00:12 unifi-controller startup-script: INFO startup-script-url: debconf: (Can’t locate Term/ 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/ line 7, line 26.)

    Nov 12 06:00:12 unifi-controller startup-script: INFO startup-script-url: debconf: falling back to frontend: Teletype

    Nov 12 06:00:12 unifi-controller startup-script: INFO startup-script-url: dpkg-preconfigure: unable to re-open stdin:

    [ 27.127267] random: crng init done

    [ 27.131931] random: 7 urandom warning(s) missed due to ratelimiting

      1. It seems I’m still getting an issue. It doesn’t look like the script is running.

        Looks like it finds the script and downloads it

        Nov 13 01:08:49 unifi-controller startup-script: INFO Downloading url from to /startup-CjurIU/tmpgH4bCM using authentication token.

        unifi-controller login: Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: E: dpkg was interrupted, you must manually run ‘dpkg –configure -a’ to correct the problem.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: E: dpkg was interrupted, you must manually run ‘dpkg –configure -a’ to correct the problem.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: E: dpkg was interrupted, you must manually run ‘dpkg –configure -a’ to correct the problem.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: E: dpkg was interrupted, you must manually run ‘dpkg –configure -a’ to correct the problem.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: E: dpkg was interrupted, you must manually run ‘dpkg –configure -a’ to correct the problem.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: Failed to stop mongodb.service: Unit mongodb.service not loaded.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: Failed to disable unit: No such file or directory

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: E: dpkg was interrupted, you must manually run ‘dpkg –configure -a’ to correct the problem.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: E: dpkg was interrupted, you must manually run ‘dpkg –configure -a’ to correct the problem.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: /startup-CjurIU/tmpgH4bCM: 168: /startup-CjurIU/tmpgH4bCM: cannot create /etc/fail2ban/filter.d/unifi-controller.conf: Directory nonexistent

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: /startup-CjurIU/tmpgH4bCM: 172: /startup-CjurIU/tmpgH4bCM: cannot create /etc/fail2ban/jail.d/unifi-controller.conf: Directory nonexistent

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: /startup-CjurIU/tmpgH4bCM: 180: /startup-CjurIU/tmpgH4bCM: cannot create /etc/fail2ban/jail.d/unifi-controller.local: Directory nonexistent

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: Failed to reload-or-restart fail2ban.service: Unit fail2ban.service not found.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: E: dpkg was interrupted, you must manually run ‘dpkg –configure -a’ to correct the problem.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: Failed to start dbus.service: Unit dbus.service not found.

        Nov 13 01:08:54 unifi-controller startup-script: INFO startup-script-url: Trying to start dbus

        Nov 13 01:09:09 unifi-controller startup-script: INFO startup-script-url: Failed to start dbus.service: Unit dbus.service not found.

        Nov 13 01:09:09 unifi-controller startup-script: INFO startup-script-url: Failed to create bus connection: No such file or directory

        Nov 13 01:09:09 unifi-controller startup-script: INFO startup-script-url: Backups to snunifistorage set up

        Nov 13 01:09:09 unifi-controller startup-script: INFO startup-script-url: Return code 0.

        Nov 13 01:09:09 unifi-controller startup-script: INFO Finished running startup scripts.

        This is everything I have with startup-script

        1. Which region/zone are you using? I just tested in us-east-1b and it works. It could be a discrepancy between zones or regions. The suggestion to use ‘dpkg –configure -a’ tells me it is the same GRUB bug.

          1. us-east1-b is what its sitting on.

            I will delete the VM and start over again, see if it changes anything.

          2. Just wanted to let you know it works now!

            I wonder if it was loading a cached version of the script then?

    1. The script will do it for you. You just enter the dns-name metadata. If the DNS name matches with the public IP, the script will acquire a certificate. If you want to download it, it is in /etc/letsencrypt/live/

  43. Hi, installed perfectly but the lets encrypt cert is saying its invalid with the reason below

    This CA Root certificate is not trusted. To enable trust,
    install this certificate in the Trusted Root Certification
    Authorities store.

    1. What OS are you using? (Win/macOS/Linux/iOS/Android/..) Which browser?

      Is the certificate really issued by “Let’s Encrypt Authority X3” and the root is “DST Root CA X3”?

      1. Hi Petri,

        cert says issued by unifi, im using windows and have tried all browsers with no luck?

        General Details Certification Path

        Serial number
        Signature algorithm
        Signature hash algorithm
        Valid from
        Valid to
        n S. ihi^rt_________________________
        UniFi, UniFi,, San Jo.
        07 November 2018 11:15:03
        04 November 2028 11:15:03
        I IniFi I IniFi i ihnf rnm ?an In

        1. So it is not the Let’s Encrypt certificate that is not trusted, but the default UniFi certificate. The script couldn’t acquire a Let’s Encrypt certificate. The most probable cause is that Let’s Encrypt server cannot connect to your instance. It could be a firewall issue. My instructions tell to allow TCP to ports 443 and 80 on the VPC for this reason. It could be a DNS issue. Does the dns-name resolve universally to the instance IP address? Universally means that from anywhere, not only on your computer. You should try it at home, work, library or on neighbour’s computer.

            1. Just to verify: You get the bad certificate warning if you just type the DNS name into you browser address bar? Like your.server.dns.

              If you want to debug this further you need to SSH into the VM command line. There is a SSH button in the GCP Console. You can view the script log with command
              sudo more /var/log/unifi/gcp-unifi.log
              Other logs that may help are:
              sudo more /var/log/letsencrypt/letsencrypt.log
              sudo more /var/log/unifi/server.log

              The other way is to spin up a new VM and transfer the IP address and the backup of your UniFi Controller to it. Keeping your fingers crossed that you’ll do something slightly differently this time. If it is a DNS problem it won’t help, though. While you are at the command line you should test how the VM sees the DNS with command
              getent hosts your.server.dns

    1. I am not able to reproduce this. Neither with a new controller or an upgraded one. Did you use my script for your setup?

      Joseph McParland in Willie Howe’s YouTube comments suggested the problem stems from the Windows CRLF combination. My script is pure Unix style LF. Thanks for the heads-up, though!

  44. Hi Petri,
    great video and instructions. Three concerns/questions:
    1. When I type nothing pops up in my browser (xxx would be the name of my DDNS name)
    2. Can I edit the values once the VM is created?
    3. Can I delete the entire project and start over? What does this do to my Lets Encrypt Cert?


    1. 1) There is something wrong with your DNS. You should check to see where does your DNS name point to. There are some web tools or local command line tools like nslookup and dig.
      2) Yes. The new metadata will take effect when you stop/start your VM.
      3) Yes. Your cert will be deleted with the VM. LE will issue you a new one. However, LE will issue only five certs per week to all servers together.

  45. Hi Petri,
    I”m not sure if I got this to work. I’m not entirely clear on DDNS vs DNS. Right now, I only have a duckdns domain. Let’s say it’s called Am I also required to have a DNS domain e.g Right now, my duckdns points to my public IP (at my house).
    Also, I’m starting from zero on this project. Do I need an old Unifi controller setup to proceed? I don’t have one. I was hoping to not have to buy a Cloud Key to get this to work

    1. The difference between dDNS and DNS is that dDNS can be updated dynamically without editing a config file or restarting a server. dDNS is used for dynamic IPs that change frequently or even occasionally. In case of GCP VMs the IP won’t change, but dDNS names are available for free. My idea was that anyone could deploy this solution without spending a nickle.
      No, you don’t need, will do fine.
      You need another DNS name that points to the IP of your controller in GCP.
      Have you read the section “How to Set Up a New Network with a GCP Controller” above? In my opinion option A is the easiest (if you have a laptop) – that’s why I put it first.

  46. Petri, amazing script. question for you. I would like to spin up an older version of the controller software, 5.6.39 or .40 because I have some legacy devices that have fallen out of support by the newer controller versions, how easy is it to modify your script to pull a specific version (this could be a useful metadata key, if it is there to pull that version perhaps?) ubnt will EOL their devices every few years and lots of people cant afford a complete upgrade for that reason…


    1. You can do it, but it requires Linux command line. After you have spun up the VM, SSH into it from the GCP Console. Remove the latest UniFi package and install the one you fancy. You need to edit Unattended-Upgrades configuration and remove the line with "c=ubiquiti"; Otherwise the controller will be automatically upgraded to the latest one on the next run. The commands you are going to need:
      sudo apt-get remove unifi
      sudo nano /etc/apt/apt.conf.d/51unattended-upgrades-unifi
      curl -o installer.deb
      sudo apt install ./installer.deb
      rm installer.deb
      sudo systemctl enable unifi
      sudo systemctl start unifi

      This is from the top of my head, not tested. Should be close, though.

      No, I’m not going to make this a metadata option. It would be a potential source for far too many errors. Anyways, you need to log on to the VM to update the controller. My target were users who are never going to SSH to the VM.

      1. you sir are fantastic, thank you very much for the prompt response and a very very very useful script. I will give this a shot, thank you!

  47. Hi Petri,
    I set up my Google VM per your instructions. Just waiting for a cert from let’s encrypt for duckdns. I do have a question: I use OpenVPN TCP443 on my router and duckdns refers to my IP address. WIll there be a conflict if both Open VPN and Google VM use port 443?

    1. You need two DNS names: One for your home router and another one that points to the external IP of the GCP VM.

      1. Sorry for my confusion, let me see if I understand:
        I need one DNS for my router ( which I currently use for OpenVPN on my router. I should not use this one when I configure my GCP VM, correct?

        I need another DNS ( for my GCP VM, and this does NOT need to point to my router. This one is used only for GCP VM, poining ot the external IP. correct?

        Lastly, If my DDNS is, is that also my DNS entry for the GCP VM? This is where I get lost. Thanks

        1. Yes. You need two names for two addresses thousands of miles apart. The should point to the external IP of the GCP VM and you use that name to access your UniFi controller.

          1. Ok,
            Thanks. The only problem is it’s not working. I must have done something wrong. When I type yyyy(not real name), nothing shows up in my browser.

              1. Does ping work or can you only ping ?

                If pinging the name doesn’t work then you need to figure out where the name points to. On a Mac you use dig and on a Windows box use nslookup from the command line.

                1. When I ping (not the IP address) I get a valid return: (Note, the IP address below is fake so I can post it, as is the DDNS name). The actual IP address IS the assigned IP by my GCP VM

                  Microsoft Windows [Version 10.0.17134.407]
                  (c) 2018 Microsoft Corporation. All rights reserved.


                  Pinging [] with 32 bytes of data:
                  Reply from bytes=32 time=37ms TTL=55
                  Reply from bytes=32 time=24ms TTL=55
                  Reply from bytes=32 time=24ms TTL=55
                  Reply from bytes=32 time=23ms TTL=55

                  Ping statistics for
                  Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
                  Approximate round trip times in milli-seconds:
                  Minimum = 23ms, Maximum = 37ms, Average = 27ms


                  1. Microsoft Windows [Version 10.0.17134.407]
                    (c) 2018 Microsoft Corporation. All rights reserved.


                    Non-authoritative answer:


                    1. If the name resolves to the external IP of your VM then stop and start the VM. If it doesn’t solve the problem delete the VM do it over. It will take 10 minutes instead of days of troubleshooting.

                    2. Deleted VM and started all over again. Still batting zero. Not sure whatI’m doing wrong.

                      When I type into the browser, I get nowhere.

                      This site can’t be reached took too long to respond.

                      Checking the connection
                      Checking the proxy and the firewall
                      Running Windows Network Diagnostics

                    3. What happens if you type the external into your browser?

                      If you want to debug this further: Create a new VM and when it finishes open it in the GCP console and click on the Serial port 1 (console) link. There you can see the story evolve. You need to click the Refresh button to see more. The lines containing INFO startup-script-url are generated by my script. Don’t worry about the lines where debconf or dpkg whine about there being no terminal.

                  2. The same thing happens when I type in the external IP address (This site can’t be reached). However, if I ping the external IP or my DNS name ( I get a valid ping return.

                    Not sure why the browser will not let me in. I’ll try establishing another VM and report back.

                    1. Double check your firewall rules and that you have typed the network tag exactly the same way in the firewall settings and the VM settings. Sounds like the firewall is keeping you out. It has kept Let’s Encrypt out as well so there will be no certificate. Once you have the tags and rules checked you should create a new VM.

                    2. I did see these entries on the start-up script. Normal?

                      Dec 5 17:36:31 unif-controller startup-script: INFO startup-script-url: debconf: unable to initialize frontend: Dialog
                      Dec 5 17:36:31 unif-controller startup-script: INFO startup-script-url: debconf: (TERM is not set, so the dialog frontend is not usable.)
                      Dec 5 17:36:31 unif-controller startup-script: INFO startup-script-url: debconf: falling back to frontend: Readline
                      Dec 5 17:36:31 unif-controller startup-script: INFO startup-script-url: debconf: unable to initialize frontend: Readline
                      Dec 5 17:36:31 unif-controller startup-script: INFO startup-script-url: debconf: (Can’t locate Term/ 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/ line 7, line 21.)
                      Dec 5 17:36:31 unif-controller startup-script: INFO startup-script-url: debconf: falling back to frontend: Teletype
                      Dec 5 17:36:31 unif-controller startup-script: INFO startup-script-url: dpkg-preconfigure: unable to re-open stdin:

                    3. Yes. Debian package installer is wondering why there is no terminal. There is no terminal because it is a headless server.

                      Did you doublecheck the firewall rules and tags?

                    4. allow-unifi
                      Unifi Firewall Rule
                      Action on match
                      Target tags
                      Source filters
                      IP ranges
                      Protocols and ports

                    5. I “may” have found the problem. My external IP is on
                      us-east1 but my VM is on us-east1-b.

                      Is that what’s causing the problem?

                    6. No. The IP is tied to us-east1 region and your VM is in zone b. You can’t attach an IP across regions.

                      Do you have the network tag “unifi” also on your VM?

                    7. Ok, I deleted everything and started over. Now, when I type in I’m taken to the following:

                      Placeholder page
                      The owner of this web site has not put up any web pages yet. Please come back later.
                      You should replace this page with your own web pages as soon as possible.
                      Unless you changed its configuration, your new server is configured as follows:
                      Configuration files can be found in /etc/lighttpd. Please read /etc/lighttpd/conf-available/README file.
                      The DocumentRoot, which is the directory under which all your HTML files should exist, is set to /var/www/html.
                      CGI scripts are looked for in /usr/www/cgi-bin, which is where Debian packages will place their scripts. You can enable cgi module by using command “lighty-enable-mod cgi”.
                      Log files are placed in /var/log/lighttpd, and will be rotated weekly. The frequency of rotation can be easily changed by editing /etc/logrotate.d/lighttpd.
                      The default directory index is index.html, meaning that requests for a directory /foo/bar/ will give the contents of the file /var/www/foo/bar/index.html if it exists (assuming that /var/www is your DocumentRoot).
                      You can enable user directories by using command “lighty-enable-mod userdir”

                    8. You need to have exactly the same network tag on the firewall rule and the VM.

                      Apparently you did something differently this time. You got connected to Lighttpd default page. What does yield?

                    9. Good! When you got your DNS name properly configured and matched the network tags.

                      I can’t say why Lighttpd isn’t redirecting you. There is nothing you could have done to cause this. There are no choices in the script for it. It is a glitch, because I have never heard anyone else running into it and I can’t reproduce it either. My suggestion is to create yet another fresh VM now that you know all the steps.

                    10. That was it!!!!! I deleted everything and started from zero.
             now goes to the unifi controller page.

                      Thanks Petri.
                      I am getting a message on my VM :Instance “yyyy-controller” is overutilized. Consider switching to the machine type: g1-small (1 vCPU, 1.7 GB memory).

                      Is this normal?

                      Thanks again.

                    11. Yeah, that is normal. Google wants to sell you a bigger VM. I admit f1-micro is sluggish but I can live with that.

  48. Need some help here –

    I am getting a certbot error –

    Dec 1 21:04:09 unificloudcontroller startup-script: INFO startup-script-url: /usr/local/sbin/ 7: [: x192.64.119.136: unexpected operator

    Dec 1 21:04:09 unificloudcontroller startup-script: INFO startup-script-url: Return code 0.

    Dec 1 21:04:09 unificloudcontroller startup-script: INFO Finished running startup scripts.

    Any thoughts?

    1. Sorry for the delay. My ISP goofed big time and stranded my servers. A total blackout for a few days and of course when I wasn’t there.

      I couldn’t reproduce this, so my advice is to delete the VM and create a new one. The error you posted is real, though. It is not from CertBot but a line from my script. It looks like the text in the script was corrupted somehow, that’s why I was concerned. In this case it appears to be a random bit error, although those are really rare.

  49. Petri,
    Question: I do not have a Unifi profile to import yet. However, I wanted to put a username and password on the Unifi splash page for added security (in case someone guessed my DNS). I set one up. Can I still import my Unifi profile into the VM? Is there any way to reset it from zero and start all over again?


    1. Hi Petri,
      just curious if there’s a solution to reset the Unifi profile and start from zero. As I mentioned, I set up a username and password to prevent someone from accessing my Unifi Controler (not yet set up) on the VM.

  50. Hello Petri, here again with some questions, I bougth a Domain name for unifi controller and try to add to unifi controller with only this metadata

    startup-script-url gs://petri-unifi/
    timezone America/Mexico City
    dns-name (provided by godaddy)
    bucket unifi-respaldo

    after installation dont work, i only acces by static ip to the controller, if i point from to static-ip it works loading static ip. Do i need to add ddns-url?

    1. No, dDNS is only for dynamic DNS. Go to your GoDaddy console and add an A record for the domain name ( with the static IP address. You probably need to stop/start the VM to acquire a https certificate.

  51. I had an issue with the backup that I used so I purged the Unifi install and then re-started the server to run your install script. Will the Let’s Encrypt cert be reused or reapplied? It has been a few days and I still get a cert warning when connecting to the controller.

    1. I don’t know what you mean by “purging the Unifi install”. If you logged on to the VM then all bets are off – I can’t know what you did. You should delete the whole VM and create a new one. The new VM will acquire a new certificate from LE.

      1. I used the backup created with the export site wizard. That backup has issues with account usernames and passwords I found out after I had imported it into the VM. So I SSHed into the server and ran “sudo apt-get purge unifi” as I did not know how else to clear a controller that I could not sign into. I then stopped and started the server so that your script would run again. I then used one of my full backups and was able to log in fine. In your notes it says that the LE part of the script will keep trying until it adds the cert but I am thinking that the cert might still be on my VM and just needs to be applied again. I am not a linux guy so I cannot read what all your script is doing.

        1. An exported site is not a full controller backup. It only contains a single site. That’s why I have instructed to download a full backup of the controller. You should try to first run the setup wizard on a new controller and then importing your site – not restoring.

          My script won’t work after you purged the UniFi package. The way UniFi Controller is written requires that the certificate has to imported into the Java environment after acquisition. Purging the controller will also delete the Java keystore. My script will just see that there already is a certificate in the LE directory so it assumes it has been already imported. It will acquire a new certificate and import it in three months. You could import the certificate yourself. Look for instructions on importing your own certificate to the controller.

          1. Waiting 3 months will be fine if I am not able to figure out how to manually import it. Thanks for your responses and the work on your script.

  52. Hello!

    I get a renewal error from certbot (real domainname changed)

    Attempting to renew cert (unifi.domain.tld) from /etc/letsencrypt/renewal/unifi.domain.tld.conf produced an unexpected error: Problem binding to port 80: Could not bind to IPv4 or IPv6.. Skipping.
    All renewal attempts failed. The following certs could not be renewed:
    /etc/letsencrypt/live/unifi.domain.tld/fullchain.pem (failure)

    Looks like lighttpd is blocking the port.

    I tried “sudo systemctl stop lighttpd” then a manual “sudo certbot renew” followed by “sudo systemctl start lighttpd” which worked.

    Any idea why the renewal script doesn’t work out of the box?

    Hyvää Joulua!

  53. Not to pile on the above issue, but just wanted to report an issue after a recent certificate renewal:

    My cerificate indicates that it’s valid until 1/25/19 and has the correct FQDN in place. I’ve verified that other Let’s Encrypt certificates I’m employing are still being reported as valid – it only seems to be a problem with my GCP-hosted Unifi controllers running this script.

    I’ll have to look into this more over the weekend, but if there’s anything I can provide to assist I’m more than happy to do so.

    Regards and happy holidays

    1. Thanks Dave and Anders for reporting this. I have run mine for a couple of years now and this has never come up. The renewals have worked like a clock. I will need to look into this. I will update this post as I resolve this.

      Och god jul till Anders och Dave!

      SOLVED: I added explicit scripts to stop and restart Lighttpd for renewals. You will get the changes if you stop/start your VM and have the startup-script-url meta-data pointing to my bucket.

      1. Just checked in and it appears to be coming up valid now – thanks a ton, Petri!

  54. I was playing around with my controller, and it seems that fail2ban is not working, I tried logging in with fake accounts like 15 times and I can still access the webpage. I have tried rebooting the server and that did not help.

    I checked Console port
    “[ [0;32m OK [0m] Started Fail2Ban service.”

    1. In Regards to the above post, I Deleted and made a new VM, Fail2Ban is working again. Thanks for all your work!.

  55. Thanks! Great work, got it up and running the second try.
    Have no idea what I did differently the second time… Now I just have to install my Ubiquiti gear and then I’m ready to roll. 🙂

    One thing in your video was faulty though and had to be corrected by myself. The center of the Universe is obviously in Sweden so I changed the timezone to Europe/Stockholm instead 😉

  56. Hi Petri,
    I have my VM set up per your instructions and the VM is up and running (very nice thanks to you). I do have a question, however; How does the VM know how to point to my home IP address? I understand the DDNS on the VM always points to the VM, but I’m unsure how the VM points to my home IP (WAN).


    1. The way it works is that the controller passively waits for the devices to make contact. That’s why the port 8080 was opened in the firewall. So you need to tell the devices to contact your VM. This is done in the the Settings > Controller > Controller Hostname/IP and also override the Inform Host. Changing this in the old controller directs the requests to the new cloud-based controller.

      1. Thanks. I assume the devices speak to the controller over an encrypted connection. Correct?

        1. Ubiquiti hasn’t really documented this. The connection is over http so it is not TLS. However the authentication is strong. When you adopt a device they exchange credentials. That’s why you can’t readopt a device without forgetting it first or manually resetting it. The same goes for the controller. It won’t accept connections from other devices it hasn’t itself adopted.

          1. That’s interesting. So all the data the devices send to the VM Controller are sent in the clear (unencrypted). Shouldn’t we be concerned about this? It seems to me anyone sniffing packets on my connection could capture the data sent to the VM. Not sure why Ubiquiti doesn’t utilize port 443 for encryption. If they did, this would be a VERY secure system. Strange. (Maybe I’m just being paranoid).

            1. Petri, you’re right. Not much info on how the AP’s communicate routine data with the controller i.e IP, Clients, Bandwidth, frequency, etc. It appears the adoption of AP’s by the controller is done via SSH. However, not clear if the routine GUI data transmitted by AP’s, switches, and Security Gateway is via SSH (although it would seem it would be).
              Here’s all I could find on how the AP’s talk to the controller.


  57. Great job setting this up. As a Linux and GCP user, I could have gone through all of this myself, but why reinvent the wheel? I did run into a problem with the certificate, however.

    The automatic attempt to set up the certificate failed with:
    certbot.errors.FailedChallenges: Failed authorization procedure. (http-01): urn:ietf:params:acme:error:connection :: The server could not connect to the
    client to verify the domain :: Fetching Connection reset by peer

    I then ran your certbot script manually:
    root@unifi:/var/log/letsencrypt# /usr/local/sbin/
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Plugins selected: Authenticator standalone, Installer None
    Obtaining a new certificate
    Performing the following challenges:
    http-01 challenge for
    Waiting for verification…
    Cleaning up challenges
    Importing keystore /tmp/tmp.4AY2AuoRuJ to /var/lib/unifi/keystore…
    The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using “keytool -importkeystore -srckeystore /var/lib/unif
    i/keystore -destkeystore /var/lib/unifi/keystore -deststoretype pkcs12”.
    Unable to import the certificate into keystore

    Any ideas?

    1. The first, automatic attempt failed because the dns-name meta-data didn’t resolve to the external IP of the virtual machine or a firewall blocked the connection to port 80. If you look at the link in the error message the ddns starts with a dot (or did you edit that?)

      The second, manual attempt first warns about the age of the code in UniFi Controller. I wish Ubiquiti would update the code to support current Java, MongoDB, keystore etc. versions. Then there is the “Unable to import the certificate into keystore” message. I have seen it occasionally, but it still did import the cert and https worked. My script would write “KeyTool import failed” if it really fails. Did you try to connect?

      Since you are Linux savvy, you can manually run /etc/letsencrypt/renewal-hooks/deploy/unifi and see where it fails. It is very straightforward script.

      1. Thanks for the quick reply. The leading dot was where this content system stripped out my angle-bracketed dummy name. So I’m still confused as to why that didn’t work correctly.

        I just checked, and the controller is indeed using the new cert now, so you are absolutely correct regarding the erroneous error message. I suspect that when I was working on this last night, Chrome was caching the self-signed cert as well.

  58. Thank you for the great write-up. Everything seemed working well so I moved two sites over. Now I’m getting an IPS alert every 2 mins from my IPS that I can’t seem to figure out. The error is only coming from one of the two sites. Do you have any thoughts on this? Thank-you

    IPS Alert 3: Generic Protocol Command Decode. Signature ET INFO Session Traversal Utilities for NAT (STUN Binding Request obsolete rfc 3489 CHANGE-REQUEST attribute change IP flag false change port flag false). From: [my-original-controller-IP]:47887, to: [google-instace.IP]:3478, protocol: UDP

  59. Thanks again for the write up.
    I had sent you a note yesterday. Just an FYI, it was the IPS settings = INFO causing the STUN Alerts

  60. Amazing writeup! I’m running into a peculiar issue now:

    I’ve had this running succesfully for a few months, and all of a sudden can no longer connect to my controller from my laptop. Interestingly, I CAN using mobile data. Same by hot spotting my laptop with mobile data.

    Did something mess up in the GCP settings? I haven’t touched them.

    1. Sounds like Fail2Ban has banned your IP. With mobile data you are using a different IP. The ban should disappear in an hour.

  61. Hi Petri,

    This worked amazingly! Thank you. Will this work for multisite as well or will more than one site push me over the limits per month of the free tier?

    1. The instance size limits the performance. It won’t go over any limit by itself. The inform messages the devices send won’t tax the server. Occasionally the controller will do summary calculations and then it will be unresponsive. The backup routine definitely locks it up for a while, but it happens in the middle of the night. As long as you can live with the sluggish performance you should be OK. The capacity is quite comparable to the 1st gen CloudKey and Ubiquiti promises it can handle up to 30 devices. After that you might want to upgrade the VM. The number of sites don’t matter.

  62. So you can run this controller 24/7 on a micro sized VM and only pay $7 a month? This $7 goes to the static public IP and not for the VM and the network traffic from the various access points? I have a Unifi controller on AWS and it’s getting expensive. I am looking for cheaper alternatives like Digital Ocean, Linode, and GCP.

    1. No, it won’t cost you $7. The IP address is not charged for when it is attached to a running VM – and the first micro sized VM in an U.S. region is free. So far I have paid for few dozen cents occasionally when I have tested changes.

  63. Hi Petri,

    Thank you very much for this write-up. I’ve been running my controller like a dream for 2 months now. I had lots of stability issues running my controller on a Raspberry Pi and I love that I no longer have to worry about that!

    I’m interested in setting up a Pi-Hole DNS server on GCP. Could this cause issues with my Unifi controller if I set it up on the same VM instance as my Unifi controller?

    Here’s the guide I’m considering following:


    1. I don’t see any reason why not. Just remove my /etc/lighttpd/conf-enabled/10-unifi-redirect.conf which will redirect all http requests to https:8443. Pi-Hole uses Lighttpd as one of its components. My Let’s Encrypt solution will disable Lighttpd for the duration of the certification renewal every three months. It usually takes about a dozen seconds. Can you live with that?

      You just need to merge the instructions from both sources. You also need to leave ports 80 and 443 open in the VPC if you want to use Let’s Encrypt certificates.

    2. Petri – thanks so much for this!

      My contribution: some instructions to hook this up to stackdriver logging. The Unifi Controller does a decent job of parsing the logs, but if you also want to access them from the Cloud Console, you need to do a few things. The painful part that should save you some work – a regex fluentd format to ingest the Unifi server logs. The rest is pretty straightforward. I did this all manually at first, then created a few lines that could be inserted into your deployment script. This builds on another contributor’s work who added the StackDriver/GCP logging agent (thanks!!).

      Once this is done, I get the following sort of output in the Logs Viewer:

      jsonPayload: {
      message: “[event] Guest[fc:18:3c:3d:XX:XX] disconnected from “MyGuestWiFiSSID” (10s connected, 0.00 bytes, last AP[80:2a:a8:02:XX:XX])”
      operation: “inform-3226”
      labels: “event”
      resource: {
      type: “gce_instance”
      labels: {
      zone: “us-west1-b”
      instance_id: “222###############”
      project_id: “unifi-controller-######”
      timestamp: “2020-07-01T03:11:32.607Z”
      severity: “INFO”

      I added the following lines just after the section that sets up the swap file.

      # Enable StackDriver Logging
      if [ ! -f /usr/lib/logging-agent/ ]; then
      echo “Installing StackDriver agent”
      if [ ! -d /usr/lib/logging-agent ]; then
      mkdir -p /usr/lib/logging-agent
      echo “Created /usr/lib/logging-agent”
      curl -Lfs -o /usr/lib/logging-agent/
      bash /usr/lib/logging-agent/
      echo “StackDriver agent installed”

      # Add fluentd config to ingest Unifi server logs
      if [ ! -f /etc/google-fluentd/config.d/unifi-server.conf ]; then
      cat > /etc/google-fluentd/config.d/unifi-server.conf << _EOF

      type tail
      path /var/log/unifi/server.log
      pos_file /var/lib/google-fluentd/pos/unifi-server.pos
      tag unifi-server
      format /^\[(?[^\]]*)\] <(?[^>]*)> (?[^ ]*) *(?[^ ]*) *- (?.*)/
      time_format %FT%H:%M:%S,%N

      echo “Added Unifi-Server to Fluentd Configuration”
      systemctl restart google-fluentd

      1. Thank you for this. It looks really good. I’ll need to test this for some time, but I will incorporate this into the script.

      2. One correction – when I pasted the script, two tags didn’t come through – either the browser is grabbing something that looks like an HTML tag, or the comment tool is hiding the text. (Or another real possibility… user error 🙂 )

        In any case, two blank lines in the configuration file should not be blank. They should have a “source” and “end source” tag like this:

        If the above two lines are blank, the same problem is happening.

        In any case, I made my fluentd configuration file publicly viewable here


  64. Great instructions and script, Petri!

    I wasn’t seeing a backup of the storage and put a cron job in /etc/cron.daily and before I realized it I copied a file as “backup”.

    Did I just kill my automated backup, and if so, can you give me the script so I can fix my error.


    1. I don’t quite get what the problem is. If you are worried that you overwrote a backup script in cron.daily, you didn’t. My backup runs from systemd and is called unify-backup.service. If you really want to manually fix something then there are links to the script at the end of the post (above these comments). My suggestion is always to download a Unifi backup, delete the VM and create a fresh controller. It takes about 15 minutes. Troubleshooting will take hours.

  65. Hi Petri,

    I just wanted to thank you for taking the time to create the script, instructions, and video on how to host the UniFi Controller on GCP. I followed your instructions and everything went as smooth as silk and it’s working great.

  66. I rebooted mine to see if it would upgrade to 5.10.12 but no dice, does it require manual intervention, or should I take a backup, blow it away and spin up a new one ? and cheers, awesome script

    1. The new version will hit the repository in about a week. It is just good news, since a few releases have been withdrawn and reverting back to a functional controller cannot be automated. Just wait for a few days, let the hotheads test drive it first… Oh, there’s no need to reboot, unattended-upgrades service will install the upgrade the night after it comes available.

      1. Petri,
        How long should we wait to update to latest Unifi?
        Any idea if newest software is ok?

        1. If you mean the controller version 5.10.12 then it usually appears about a week after it has been announced. Quite often it has appeared on a Monday, but that is not a rule. You don’t need to do anything, it will be installed automatically. If you have a session open then you will need to reauthenticate.

  67. Hey, Petri!!
    thanks again for the guide!

    I ran this setup about 6 months ago and everything went great!
    but I tried to access my controller yesterday and my unifi account is offline.
    i try to access the controller from my custom URL you showed me how to create and it said the website is unavailable.
    any recomendations of what do do about this?
    If i go to the actual IP of the controller i can access it but not from the unifi controller.

    1. Do you mean you can’t access it through ? That is meant as a backdoor to controllers behind a NAT firewall. It based on WebRTC remote desktop connections and is flaky at best. I struggled with that until I started using GCP and AWS based controllers. There is no need to go through the Ubiquiti service since you can always access your controller directly by its DNS name. I don’t even bother to set up the “Cloud Access” on controllers anymore. Once in a while Ubiquiti announces that they have fixed some problems and improved it, but I really don’t need it any more.

      If you mean you can’t access your controller by its DNS name but the numeric IP address works, then you need to fix your DNS name to resolve to the IP address.

  68. Hi Petri,

    Thanks for creating this script. I had it all setup and running great. Today I connected to the cloud instance through SSH and made some updates to Java. I then issued a “sudo reboot” command and when the system rebooted it was no longer using the Let’s Encrypt certificate while it then at the same time also had upgraded to the new 5.10 firmware. Is this expected behavior and is there a different process I should follow?


    1. No, that is not expected. I have no idea what happened when you made manual updates. I wrote the setup so the user never needs to log on. Probably the Java certificate keystore has been overwritten. I would suggest to create a new VM and restore the latest backup at the time it was still working. You could try to run /etc/letsencrypt/renewal-hooks/deploy/unifi manually (as root) to see if it can fix the Java keystore.

      1. Thanks for the suggestion. I tried the renewal-hook and got the error that it was unable to import certificates into the keystore. I will restore a backup into a new VM.

        1. Try to log on. The Java keystore thing is a mystery to me. Occasionally it just works even though it just said it didn’t.

  69. Hi Petri, thanks again for sharing this great job.

    I’m using your script since april 2018. Everyting PERFECT. Now, I’m stuck in 5.9.29.
    Everytime I login I see “Controller software update 5.10.17 is now available” in top left corner. script is ejecuted at startup:
    Feb 15 21:57:57 unifi-controller startup-script: INFO startup-script-url: Return code 0.
    Feb 15 21:57:57 unifi-controller startup-script: INFO Finished running startup scripts.

    I tryed deleting “/usr/share/misc/apt-upgraded-1” and running “sudo ./” but system is still in 5.9.29

    Content of “/etc/apt/apt.conf.d/51unattended-upgrades-unifi” is:
    Acquire::AllowReleaseInfoChanges “true”;
    Unattended-Upgrade::Origins-Pattern {
    Unattended-Upgrade::Remove-Unused-Dependencies “true”;
    Unattended-Upgrade::Automatic-Reboot “true”;

    I don’t know where else to check if autoupdates is working… any clue? xD

    Thanks again!

    1. If you want to see how Unattended Upgrades is working the logs are in /var/log/unattended-upgrades.

      I think I know what is the cause. When Ubiquiti changed their domain name to they also inadvertly moved their repository behind TLS, which is just silly. The packages are signed with PGP, there is no need for TLS. Standard APT doesn’t know how to download over https. I changed my script to install yet another package, but it only applied to new installations. Now the latest version will apply the package to all installations. You just need to Stop/Start the VM in GCP Console and it will upgrade overnight.

      1. Thnaks Petri!
        I check /etc/systemd/system/ and it was also ok, That make me think autoupdate works right… I ran “apt show unifi” and discover 5.9.29 was avaible version.

        apt-get update displayed “E: The method driver /usr/lib/apt/methods/https could not be found.
        N: Is the package apt-transport-https installed?”

        I ran “apt-get install apt-transport-https” and now current unifi version is avaible.
        Next autoupdate will work fine.

        Thank you!

  70. Hi,
    I’m having trouble getting the lets encrypt certificate. The VM is set up correctly, but it defaults to the self signed ubiquiti certificate (vice lets encrypt). I’ve deleted and rebuilt the VM as well as start and stop VM. Still no Lets Encrypt certificate. Any ideas?

    1. There are two requirements: First, the dns-name metadata must globally resolve to the external IP of you VM. Let’s Encrypt will use that DNS name to resolve the IP from their datacenter wherever that is. Does the DNS name you provided resolve from your cell phone, work laptop, library PC etc? The second requirement is that the VM must be reachable from the aforementioned datacenter to ports 80 and 443. Quite often users forget to attach the correct network tag to their new VM.

      If you are Linux savvy, you can press on the SSH button in the GCP console and use command sudo more /var/log/letsencrypt/letsencrypt.log to see what the logs tell you.

  71. Hmmmm,
    stat of /var/log/letsencrypt/letsencrypt.log failed: No such file or directory

    1. That means that CertBot hasn’t run ever. Did you supply a dns-name when you created your VM? Does is resolve globally? Which DNS provider are you using?

  72. User Operator Error (Doh!). I should have read more closely what you were telling me. I did not update my DDNS VM External IP address. It is now updated and resolves properly.

    However, if I type in my DDNS address (with no port entry) it does not connect. But if I type in my DDNS:8443, it does connect and has the correct SSL. Any ideas?

  73. You are correct Sir. All is well. Thanks. Great write-up. This will be fun to use.

    1. Petri,
      Many thanks for this write-up. Is it possible to run the lets encrypt script on a Linux based router? It would be cool to have my own Lets Encrypt Certificate for my house, that automatically renews. I’m a Noob with Linux, but can follow instructions well.



      1. You don’t need the script. Just install CertBot and run it manually. Google for instructions on the site. Do note, that the Let’s Encrypt servers need to reach the Linux box inside your home firewall. You need to set up port forwarding for ports 80 and 443 and a resolving DNS name. If you don’t have a static IP you need some kind of dynamic DNS to track the changing IP. All this is left as an exercise for the reader.

  74. Hi there.

    I’ve gone through the setup twice now to make sure that I’m not missing anything, but something isn’t working. It seems to be partially working, because I get redirected to the https version of the url, etc. when I try to connect, but then it just spins for a long time and then never connects. I’m not sure what I’m doing wrong, and I don’t know how to troubleshoot what might be going wrong.

    Any thoughts on how I can troubleshoot why I can’t get the controller to come up in the browser? It just keeps telling me that my site took too long to respond.



    1. p.s. I noticed that even though I get redirected, there is no certificate set up either. And I control my own domain, so it’s not a DDNS issue.

    2. It works for me ? I just tested by creating a new test controller. I do it once in a while since my script depends on so many other packages and services that change frequently.

      Your problem isn’t caused by Fail2Ban since you can connect to port 80 (to be redirected). My guess is it is the firewall rules. Do you have them set up correctly in the VPC Network section? What is the target tag you used there? Have you applied exactly the same network tag to the VM? (Not the rule name!) An error there would cause the symptoms you are experiencing. If you find the mistake, don’t try fix it, but create a new VM without the flaw.

      1. Hi there.

        Yes, I did everything twice. I did everything exactly as you did, including the names. The tag is “unifi”, and that’s what I applied, and I coped the settings from this page. 🙂

        I guess I’ll try a third time and see what happens.

        Thanks. I really appreciate your reply. I hope I can figure it out.

        1. I just checked, and everthing looks correct. I did however find this message when looking at the VM in the console, so it looks like something is causing the instance to work too hard and that must be why it’s timing out.

          “This instance is overutilized. Consider switching to the machine type: g1-small (1 vCPU, 1.7 GB memory). Learn more”

          But I’ll try again

          1. Third time is the charm, isn’t it. Don’t worry about the warning, all my controllers show it. Consider it as the sales pitch at McDonald’s: “Do you want to super size your meal?”

            1. Ok, Thanks.

              I tried a third time, but still no luck, I can’t figure out what I’m doing wrong, but it just keeps timing out. I triple checked everything, and I can’t find any mistakes. Maybe I’ll try another Region for everything.

              1. No luck in a new region either. I feel like there is some kind of network issue, but for the life of me I can’t figure out what. 🙁

                1. No, it is not the region. You keep doing some mistake and you are blind to it. I would suggest you get someone to do it for you. S/he doesn’t have to be a computer genius, just as long as s/he is not afraid of computers. Let her/him do it with the instructions above without your help. Many (most?) have succeeded, you just had bad luck.

                  1. Yeah, it must be something simple I’m missing. But I’ve done it 5 times now, and I check every single step.

                    Oh well. I’ll figure it out. Thanks.

        1. Also I get this:

          ~$ curl
          curl: (60) SSL certificate problem: unable to get local issuer certificate
          More details here:
          curl performs SSL certificate verification by default, using a “bundle”
          of Certificate Authority (CA) public keys (CA certs). If the default
          bundle file isn’t adequate, you can specify an alternate file
          using the –cacert option.
          If this HTTPS server uses a certificate signed by a CA represented in
          the bundle, the certificate verification probably failed due to a
          problem with the certificate (it might be expired, or the name might
          not match the domain name in the URL).
          If you’d like to turn off curl’s verification of the certificate, use
          the -k (or –insecure) option.

          1. I don’t know wether this will amuse you, but your controller appears to be working great. I can connect to the wizard page just fine. No certificate errors. It has a beautiful LE certificate valid until June 8th. Probably all your prior installations also worked just fine. There is something wrong with the OS/browser you are testing with. Try another computer.

            1. Ugh! I think I just figured that out.

              I can get to it from my phone, but now from my Chromebook for some reason, which is where I do most of my work from.

              Thanks for your patience! Sorry to have bothered you for nothing.

              1. Ok, now the plot thickens. So it turns out that I cannot get to the VM from inside my own network. But I can get to it from my phone, which is using cellular data, and from home, but not from my work network. Very strange!

                Sorry for all of the drama, but it’s clearly an issue with Cox internet here at the buisness.

  75. Petri,

    I followed your procedure but now when i go to my domain it takes me to my local routers login screen, edgemax login screen
    How do i fix that?

  76. Hello, Thanks for putting this guide together! I’ve had my controller running now for couple weeks and yesterday it stopped. I logged in to GCP and i have the recommendation to “increase perf, This instance has had high CPU and memory utilization recently. Consider switching to the machine type: g1-small (1 vCPU, 1.7 GB memory)

    What could be causing this error and how can i get my controller running again?

    Couple of days ago, i tried to download a setting snapshot from the controller and it wasn’t working… then i realized it was probably downloading the file to my instance. I tried about 4 times before realizing my mistake. Could that be the issue and if so, where can i go to to delete the files to free up space?

    Thanks for any advice!

    1. What do you mean stopped? Do you mean it is in stopped state in the GCE console? In that case you can start it from there. I’ve never seen a controller to stop itself in that way, though.
      If you mean the controller is not responding, then the only supported remedy is to stop and start the VM from the GCE console.
      Have you had the browser window open all the time? In that case the browser may have been upset by the upgrade earlier this week (to 5.10.19). Close the browser and open it again.

      The suggestion to upgrade is reasonable. The micro VM is sluggish, but it is free. Google is only loosing money as long as you keep using it.

      If you downloaded the backup from Settings > Maintenance > Backup > Download Backup then it should be downloaded to your workstation, not left on the server.

      1. Thank you!!!! a Start/Stop got it working again. I was worried the new updates caused to the controller to become too large/processor intensive for the free tier.

        Thanks again for the quick response!

    1. I’m sorry but my script doesn’t support this. You can use my script as a starting point, though. After the script finishes you can log in via SSH (there is a button in the console). You can use sudo apt-get remove unifi to remove the installed UniFi package and install the one you fancy. Remove line "c=ubiquiti"; from /etc/apt/apt.conf.d/51unattended-upgrades-unifi or your installation will be automatically upgraded the following night. You need to keep your UniFi package updated manually.

        1. sudo nano /etc/apt/apt.conf.d/51unattended-upgrades-unifi
          You need to use sudo for installs, uninstalls and configuration edits.

  77. First of all thanks for writing this guide and setting up the video. It was very easy to follow and setup and I have been running this smoothly except for one exception which I will point out since December.

    Now for the exception: It appears that the dynamic DNS piece only runs when the server restarts (or is first started). Can you update it so that it automatically runs every so often? If this is already the case could it be that dynoDNS resets the DNS values BEFORE the VM does?

    1. The DDNS service should keep the same IP address until reset. The VM will keep its IP address forever if you have set up a static IP. Even if you are using a non-static IP it will remain until the next boot. That’s why my script only updates it at the boot. All other scripts I have looked at (for home servers etc.) monitor the external IP address and reset the DDNS service only if the external IP changes. In most cases it will be never or a very long time.

      I am curious: What IP does your DDNS service reset to?

  78. I have 3 custom domains hosted on pointing to a local NAS web server. I have those configured in Unifi to update via

    I also have a subdomain we’ll call it I have configured in the GCP VM. So I guess what might be happening is the Unifi update to is also updating the controller’s DNS entry and making it my local ISP assigned IP rather than the one hosted on GCP.

    I guess I answered my own question haha. In order to avoid this behavior I would probably need to specify each domain separately in the Unifi configuration so that it does not blanket update them all via the Unifi controller if that makes sense.

  79. Petri,
    I have used your setup/configuration since last year October 2018. It has been running great! It is at 5.10.19_11646 as well.

    I always use GCP console and click SSH to the VM to check things around.

    My fail2ban.log is big and it seems a lot of try out there on the internet (almost every few mins I see an IP tried to SSH with an invalid ID) .

    Do you know which setting I need to disable this?

    Please advise,

    1. Disable what? Fail2Ban? Logging? Hackers?
      By default GCP has disabled password logins altogether so you don’t need to worry about scripts trying different passwords. Every time you click on the SSH button a new key is generated and the key is valid for the duration of the session.
      You may regard the login attempts as burglars trying which car doors are open. There is no way you can prevent it. However, as long as you keep your car doors locked there is no harm done if someone tries the handle.
      If you want to rotate Fail2Ban logs more frequently edit /etc/logrotate.d/fail2ban

      1. Petri,
        I am trying to see if Port 22 can be blocked facing the internet since inform don’t use that port to monitor the unifi system. That way less I/O being used with those hackers trying on a minute basis.

        1. Don’t block port 22 or you won’t be able to use the SSH button! I don’t know if Google has published the address ranges used by the Console. You could allow only those and hope they won’t be adding more addresses. If they do and your future Console is on one you won’t be able to log on.

  80. Thank you Petri for the write out! Just set up the controller on GCP. Everything is running flawlessly.

    For people use Cloudflare to manage their domain, you may encounter “STUN communication failed” alert, remove proxy and use DNS only would help solving the problem. I spent a full hour to figure this out…

    1. Yeah, I would expect reverse proxying causing trouble for the Controller. Thanks for reporting this. Hopefully someone will avoid this trap.

  81. When I log into the controller, i get a notification that 5.10.20 is available.
    Isnt the controller suppose to auto-update to latest version?

    1. The autoupdate runs at night after the publication. In the worst case you need to wait 23h59m for the update. Mine is already upgraded.

  82. Hi,
    Thank you so much for this! Did it with my personal UniFi two days ago and am doing it for our company UniFi today.

    Everything worked perfectly at home where I have Cloudflare DNS and just added an A-record that pointed to the public IP of the VM.

    Here at our company we don’t use Cloudflare – but I again just added an A-record unifi.yyy.yy that pointed to the public IP of the VM.

    But Let’s Encrypt doesn’t work! When looking at the logs I get:
    simon@unifi:~$ sudo more /var/log/letsencrypt/letsencrypt.log
    2019-03-25 14:43:16,545:DEBUG:certbot.main:certbot version: 0.28.0
    2019-03-25 14:43:16,546:DEBUG:certbot.main:Arguments: [‘-q’]
    2019-03-25 14:43:16,547:DEBUG:certbot.main:Discovered plugins: PluginsRegistry(PluginEntryPoint#manual,PluginEntryP
    2019-03-25 14:43:16,557:DEBUG:certbot.log:Root logging level set at 30
    2019-03-25 14:43:16,558:INFO:certbot.log:Saving debug log to /var/log/letsencrypt/letsencrypt.log
    2019-03-25 14:43:16,560:DEBUG:certbot.renewal:no renewal failures

    Any idea what’s wrong?

    1. Does the DNS name resolve globally (instead of just in the company internal network)? Try nslookup of dig from your home.
      That log looks innocent enough, no errors. It looks like Let’s encrypt is happy. Are there certs in /etc/letsencrypt/your.domain.dns/live ?

      1. Doing an nslookup looks fine (it says “Non-authoritative answer: Name: Address: IP-ADDRESS.

        In /etc/letsencrypt there is only a file and a folder:
        file: cli.ini
        folder: renewal-hooks

        1. What happens if you type the DNS name into your browser? Do you get the UniFi Controller after a certificate warning?
          For some reason the script didn’t get a certificate. If the external IP doesn’t match the DNS name then it won’t even try. Let’s Encrypt will only provide five certificates per week for a given domain. This could be an issue if you use some popular DDNS domain or if you keep creating VMs for some reason.
          I would suggest to delete the VM and create a new one (unless you are going over the limit of five).

          1. Yes, when typing the domain in a browser it continues to the UniFi controller after the certificate warning.
            I created an A record on my DNS server – is that the correct way of doing it?
            I will try to delete and create a new one later. Thanks!

            1. Is your VM behind a reverse proxy, NAT or such thing? The VM compares its external IP address to the resolved DNS name IP. If they don’t match (i.e. the DNS name doesn’t point directly to the external IP address) it won’t even try. You can delete this test from /usr/local/sbin/ if necessary.

  83. Hi Petri

    Thanks a lot for your efforts, it works like a charm!

    Have you considered a supplementary tool to install UniFi video as well? – or expand the current script with a flag to include the UniFi video bits…

    Again, thanks for sharing this.


    1. No, I don’t have videocams so I wouldn’t be using it myself. I wouldn’t notice problems or have any means of testing.

      You can do it, though! Feel free to fork off a branch on GitHub and add some functionality. You’ll learn a bunch and make new friends.

      Or you can just log on to your VM and install the video controller manually. If my memory serves me right you need to edit the firewall rules a bit. You can even uninstall unifi package if you just want to use the base system as a starting point.

  84. Hi Petri,
    Everything working great except I am getting the STUN triangle warning on the device page. I know I have to port fwd 3478. However, what device do I fwd port 3478 to?
    I tried to fwd 3478 (from my home router) to my switch on my LAN 192.168.XXX.XXX. However, That doesn’t do the trick. I also tried to fwd the port using my ddns address, no Joy. Also tried to fwd 3478 using the IP of my Google VM. Still no joy. Any ideas?


    1. No, you don’t need any port forwarding. All your devices should have the GCE VM as their inform address so they all report directly there. You can check the current settings by SSHing to the IP of one of your APs and issuing command info. Look for the (last) line “status”.

        1. There are a few explanations:
          – Do you have double-NAT? This could happen if you have an USG and your modem is routing as well. This can be remedied by configuring the modem to bridge instead of routing.
          – Is port udp:3478 certainly open in your VPC firewall? (Not tcp:3478)

          I have seen this occasionally, but it has been transient. Fortunately most of the functionality doesn’t require STUN. You’ll miss opening terminal sessions from the controller and the ability to blink the LEDs (locate function) from the controller. See Troubleshooting STUN Communication Errors

          You may want to verify the settings by SSHing to an AP and using command more /etc/persistent/cfg/mgmt. Check inform URL mgmt.servers.1.url and stun url.

          1. Hi Petri,
            here’s what the SSH output looks like:

            Any ideas?

            1. That is what I expected. I’ve never seen a stun url mismatch. But there may come the first time.

              Have you double checked the GCP VPC firewall rules? Do you have a routing modem? Is there any chance of double-NAT? Is your modem bridged? Some ISPs do NAT for their clients. It is known as carrier-grade NAT (CGN) or large scale NAT (LSN).

              This problem is specific to your GCE VM or your network. I can’t reproduce it and there are no other reports. There are limits to what I can do from here.

              1. Hmmmm,
                GCP VPC firewall rules allow UDP 3478. I have an Arris DOCSIS 3.1 Modem connected to the Cox domain. My modem is connected to an ac router. Not sure what gives.

                1. Is your modem bridging or routing? Does you ISP use CGN? My bet is on double-NAT somewhere along the line. But you can just ignore it and live with it.

          2. Hey Petri and Frank,

            Were you able to fix your STUN issue?

            I am having the same issue with my controller hosted on Google Cloud.

            Here is what I have tried so far:

            1. Changed STUN Port in controller in GCP ( but I changed it back) and uncommented ports per this article on page 14.
            2. Then I tried to enable dhcp option 43 on my router by following the instructions here to convert

            Convert the controller’s public IP to HEX and add 0104 to the front and putting the below in the lan interface settings of my router running openwrt.
            list dhcp_option ‘43,01:04:22:79:5A:2A’
            3. Controller version one of the UAP-AC_LR firmware version:

            4. truncated Output of cat /etc/persistent/cfg/mgmt:

            5. Verified that egress and ingress for 3478 is allowed in firewall rules in GCP
            6. Debian version is 9.13 , stretch

            Not sure if I should set DHCP option 15 in my router.

            Do you have any other troubleshooting suggestions?

  85. is ther any way to set up a vpn for internet or other stuff on the same vm since i doint want to pay for a anotehr vm as unfi controlor

    1. Yes, you can certainly set up the VM as you like. Note that the micro-sized VM is struggling as a UniFi controller already, as you can see from the sluggish performance. You can add some lightweight service or some intermittent loads as you see fit. If you need a bigger VM you will need to pay for it anyway.

      1. Petri,
        The micro-sized VM seems to work fine for me. Do you think with future Unifi upgrades, the micro-sized VM will no longer adequately support the controller?

        1. Ubiquiti has sold so many original Cloud Keys they can’t let the controller grow bigger than that. The micro VM is performance wise close to the CK (but the disk is better).

  86. Hi Petri, thanks much for the guide, worked really nice for me. I had a issues with the script because i copied the url but didn’t see the blank space “gs: //”, maybe it’s a good idea delete the blank space this for avoid future problems for people who’s follow your guide?

    1. I couldn’t spot anywhere a space after the gs: Where did you find it?

      1. Google traductor generate that blank space, i used that for translate this website (to spanish). I just realized, I’m sorry for the misunderstanding. Regards

  87. Hi, It worked like a charm- great work, however i skipped the dynamic dns part and did not enter those two lines of key values of dns name and dns url when setting it up , thinking I can take care of it after it’s all setup. Now the question is there a way to do after having the controller setup and up and running or do i have to delete the VM and start again?

    1. You can change the metadata anytime. Then just stop/start the VM to run the startup-script.

  88. Hi Petri,

    Great work, thanks for this. ‘Step 3. Set Up the Virtual Network’ does not currently include tcp 22, is that an error?

    1. The controller is safer if you don’t allow SSH from the Internet. I wrote my script for people who don’t want to type Linux shell commands.
      You can access the command line from the GCP Console. If you want to allow SSH access from the outside you also need to set up a permanent account on the VM (and allow password auth if desired).

      1. Okay thanks, was reading some of the comments and saw a reference to port 22 so I was asking.

  89. I setup everything like you show on your video and guess what :


    A big thanks you from Montreal…


  90. Petri,
    for some reason my controller is stuck on “UniFi Controller is starting up…please wait a moment” I restarted the VM and it’s still stuck on this message. Any idea whats wrong?

    1. Hard to say from here. The fastest solution is usually to deploy a new VM with the same IP and restore the latest backup from the bucket. If there is something wrong with the VM it will usually give more trouble as time passes.

        1. It depends. If you are recovering from an operator error then go to Settings > Maintenance > Restore. If you are recovering from a system error then it is best to create a new VM. On the first page of the setup wizard there is an option to restore a backup. There is no point in trying to resurrect a broken controller when you can create a new one in 10 minutes flat. Download the latest backup from Google Cloud Console at Storage > Browser.

    2. Hi all I had this same issue and just started and stopped the VM and it now works again. I noticed a message on the google console about that there was high cpu usage detected though. @Metis any ideas what could cause this?

      1. Google wants to upsell you a bigger VM. It could be that the CPU spikes for some database compression tasks, but I haven’t seen it ever. Usually it sits around 10-15%.

  91. Just want to leave a thank you for the great guide. I set mine up earlier this week and it’s working flawlessly!

  92. Hi Petri,

    Google recently sent out an email about the JSON API called “New endpoint for the Google Cloud Storage JSON API”. In it the following is stated:

    What do I need to do?
    If your production or test code doesn’t check for endpoint-specific details, no action is required on your part.

    If your production or test code checks for endpoint-specific details, you will need to modify them before June 20, 2019 as follows:

    If your code checks that the ‘baseUrl’ or ‘rootUrl’ fields in the JSON API Discovery document point to, you will need to modify those checks to allow either or Note that the oauth2 scopes fields in the Discovery document will not change and will continue to point to
    If your code checks that the ‘selfLink’ field in bucket or object metadata points to, you will need to modify that check to allow either or
    If you access Cloud Storage through a firewall, you will need to ensure that requests to are allowed by your firewall rules.

    Is there something that needs to be done. Can you comment? Thanks for your great script and feedback!

    1. No action is needed. The change is at a very low level and my script uses a high level command written by Google. If the high level command needs some changes then Google will take care of it.

  93. Is it possible to change dns to the VM without reinstalling it?
    It seems the free has gone down and I would like to switch to another one.

    kind regards

    1. Yes. The metadata is applied at boot, so change the metadata and stop/start the VM from the GCP console.

  94. Many thanks Petri, I have been using the GCP hosted UniFi controller for a few weeks now by using your script (previous controller was on AWS).

    Up until very recently I was using a subdomain from but I’ve since created a subdomain from my own hosted domain, altered the metadata field to reflect the changes & all is working perfectly upon starting the VM again, LE cert is valid etc etc.

    Very happy with this controller solution made super easy by you 🙂

  95. Hi Petri,

    Thank you again for a great tutorial.
    My controller upgraded last night to 5.10.24 and now I’m getting flood by this error on two of my AP: “Message: AP[xx:xx:xx:xx:xx:xx] /usr/sbin/dnsmasq exited with code 512 and restarted by inittab”

    Can I restore to previous backup from within the controller interface? settings:maintenance:retore:choose file?
    Or do I need to do the whole installation all over again?
    Also how can I disable the automatic update of the controller?

    Thank you very much.

    1. Hmm, mine is running 5.10.24 just fine, but YMMV of course. There is no easy way to downgrade. My suggestion is to create a new VM and restore the latest backup there. The new install will be 5.10.24 as well.
      If you really need to go back you’ll need to login via SSH in the GCP console. Uninstall by sudo apt-get remove unifi and then install your favorite edition manually. To prevent the automatic upgrade next night you’ll need to edit or remove /etc/apt/apt.conf.d/51unattended-upgrades-unifi.

      1. Thank you Petri. I think I’ll wait for the next update to resolve this. Actually the controller is giving warnings and not errors. But the warnings are generated every few seconds. I have switched out the option to send this warnings by email otherwise my inbox would not survive.
        I’m not the only one though, there are a couple of threads on the internet about this in different forums.
        Is there a line I can comment out in 51unattended-upgrades-unifi so it won’t upgrade and I can just un-comment the line when I want to upgrade?
        I’m sorry I’m not much of a coder

        1. The line with “c=ubiquiti” is the one that keeps UniFi controller up to date.

  96. Petri, a question for you sir. I love how everything is working, however if I have started to outgrow the free tier from google and wanted to say upgrade the VM, would it be a simple case of increasing the ram / cpu ? would any changes have to be made to your script with regards to its swap file usage etc ? I have started to add more and more devices and I worry the spec might being to have issue coping ?

    1. Yes, it will work just fine. If you give your VM 2GB or more memory the script won’t create a swap file. That is the only difference the VM size will cause.

      1. fantastic! have you seen any limits with the micro VM in terms of clients / adopted devices ? when you start to notice performance related issues ? just want to make sure thats whats happening, I see some devices missing heartbeats and coming straight back, with no change in uptime

        1. I don’t know effect of load or the load components for the controller. It does get sluggish, it will lock up occasionally during backups and database compression, miss some heartbeats, but nothing really serious. I’ve recommended the same 30 device limit that Ubiquiti gives for the first gen Cloud Keys. The performance is similar so I’d expect similar capacity.
          Ubiquiti has published some papers on high volume controllers. You may want to read those. They have factored the device count, captive portal, different authentication schemes and so forth.

          1. Just seeing this now. I wasn’t aware of the 30 device limit. I have 43 devices (20 are wireless dimmers) and everything seems to be working fine. Am I asking for trouble with this many devices?

            1. No, you are good. The devices only send their status once a minute. In your case more than a second apart on the average. You may miss a few once a day when the backups are run and some database compression occurs. If you run a captive portal with plenty of users then it is another matter altogether. See How to Tune the Controller for High Number of UniFi Devices for more info.

  97. Hi there! First of all thank you for taking the time to write this up.
    I’ve got everything up and running smoothly, or so it appears. The only quirk is when trying to test fail2ban. I used a VPN and I was able to enter a fake username/password into the login screen numerous times without triggering an IP ban.

    Is there a way I can check if fail2ban is working? I’m a bit novice with linux/ssh but I can see that there is a unifi-controller.conf in the fail2ban/jail.d directory, but not sure if the jail is active or not.

    Also it does not appear that the backups are going to the storage folder at all. How can I check the backups are being stored correctly?

    Any help would be appreciated, I’d like to avoid re-doing the VM but if I have to, do I just need to backup my existing config from within the controller and then reload it on a fresh VM? Will my devices automatically be picked up?

    Thank you in advance.

    1. Also, I can see in the fail2ban the logs are showing “Found: ##IP##” but the ban does not trigger.

      1. The easiest way to test is to try to log in with wrong credentials three times. After that you’ll still get the login page (it comes from your browser’s cache) but the log in just hangs. Normally you’d get a failure message. After an hour it will open again. I haven’t tested this lately. I follow my logs and I haven’t seen any scripts trying to log in to my controllers.

      1. I am having the same trouble with backups not completing on their own. I browse the bucket and there was nothing there. I logged into the controller box on GCP and ran the command I found in the service file. It failed when I was an unprivileged user but if I sodo it works from inside the box:

        sudo /usr/bin/gsutil rsync -r -d /var/lib/unifi/backup gs://my_bucket_name

        The logs do show it initiating near the 1am time:

        ug 4 01:18:47 unifi-controller-main systemd[1]: Starting Daily backup to org_tom_unifi_backups service…
        Aug 4 01:18:53 unifi-controller-main gsutil[10601]: Building synchronization state…
        Aug 4 01:18:55 unifi-controller-main gsutil[10601]: Starting synchronization…
        Aug 4 01:18:55 unifi-controller-main systemd[1]: Started Daily backup to org_tom_unifi_backups service.
        Aug 4 01:18:55 unifi-controller-main systemd[1]: unifi-backup.timer: Adding 10min 16.622564s random time.
        Aug 4 01:18:55 unifi-controller-main systemd[1]: unifi-backup.timer: Adding 18min 29.025547s random time.

        I am unsure how to make this work. It is one of the most important things for me as the thing that drew me to run this in the cloud was consequences I had from a power outage at home. All of the sudden, I care *a lot* about backups. 🙂

        Thanks in advance!

        1. Systemd runs the units as root.
          Apparently you have found the unit files in /etc/systemd/system and they are OK. Have you checked /var/lib/unifi/backup/autobackup that the backups are created?
          I can’t tell.. Are you certain there is no autobackup folder in the storage bucket? The periodic backups are inside.
          (Yes, backups are golden!)

  98. Petri-
    Installed this last year and was working great until today, something went belly up, couldn’t access the software by IP or DNS. Decided to delete the VM and start over, same issue, Safari can’t open the page “https://[GCP-IP]:8443” because Safari can’t establish a secure connection to the server “[GCP=IP]”

    I’ve checked the serial logs and looks like Unifi is up on the VM. Anything else I can look at directly? Appears that Lets Encrypt did its thing correctly from the logs too.

    unifi-controller login: Jun 16 18:07:07 unifi-controller unifi.init[677]: Starting Ubiquiti UniFi Controller: unifi.
    Jun 16 18:07:07 unifi-controller systemd[1]: Started unifi.
    Jun 16 18:07:19 unifi-controller startup-script: INFO startup-script-url: OKDynamic DNS accessed

    1. That sounds like a certificate issue. Try accessing your controller with Chrome. Chrome lets you bypass the bad certificate warning. Then you can click on the lock icon in the address bar to see what’s wrong with the certificate.
      The domains I used in the video are not available any longer. The owner decided to withdraw them. It could be your computer has kept the IP address in memory, but Let’s Encrypt renewal process can’t work.

      1. I tried Chrome, ERR_CONNECTION_RESET error. I just created a new VM, again. Here are the fresh logs, does seem cert related. I am using DuckDNS

        Jun 16 18:43:33 unifi-controller startup-script: INFO startup-script-url: Saving debug log to /var/log/letsencrypt/letsencrypt.log
        Jun 16 18:43:36 unifi-controller startup-script: INFO startup-script-url: Plugins selected: Authenticator standalone, Installer None
        Jun 16 18:43:36 unifi-controller startup-script: INFO startup-script-url: Registering without email!
        Jun 16 18:43:37 unifi-controller startup-script: INFO startup-script-url: Obtaining a new certificate
        Jun 16 18:43:38 unifi-controller startup-script: INFO startup-script-url: Performing the following challenges:
        Jun 16 18:43:38 unifi-controller startup-script: INFO startup-script-url: http-01 challenge for [redacted]
        Jun 16 18:43:38 unifi-controller startup-script: INFO startup-script-url: Waiting for verification…
        Jun 16 18:43:41 unifi-controller startup-script: INFO startup-script-url: Cleaning up challenges
        Jun 16 18:43:45 unifi-controller systemd[1]: Starting Lighttpd Daemon…
        Jun 16 18:43:45 unifi-controller systemd[1]: Started Lighttpd Daemon.
        Jun 16 18:43:45 unifi-controller startup-script: INFO startup-script-url: unable to write ‘random state’
        Jun 16 18:43:47 unifi-controller startup-script: INFO startup-script-url: Importing keystore /tmp/tmp.fk0W8Gzfsk to /var/lib/unifi/keystore…
        Jun 16 18:43:47 unifi-controller startup-script: INFO startup-script-url: Warning:
        Jun 16 18:43:47 unifi-controller startup-script: INFO startup-script-url: The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using “keytool -importkeystore -srckeystore /var/lib/unifi/keystore -destkeystore /var/lib/unifi/keystore -deststoretype pkcs12”.
        Jun 16 18:43:47 unifi-controller systemd[1]: Stopping unifi…
        Jun 16 18:43:52 unifi-controller unifi.init[26905]: Stopping Ubiquiti UniFi Controller: unifi.
        Jun 16 18:43:52 unifi-controller systemd[1]: Stopped unifi.
        Jun 16 18:43:57 unifi-controller startup-script: INFO startup-script-url: Unable to import the certificate into keystore

      2. I just created a completely new GCP project, new IP, and tried using afraid DNS, same exact issue. Unable to import cert into keystore. Any ideas?

        1. The “unable to import cert to keystone” is expected. It still works. I haven’t found out what causes the error. UniFi controller is using really old Java mechanisms, which could cause it. The ERR_CONNECTION_RESET is a different matter. It has nothing to do with the certificates.
          I’ll spin up a new test instance tomorrow. It is getting late over here.

          1. Ok, this is sort of figured out but not confirmed. I plugged in a new AP and it seems this is when GCP decided to stop hosting me. The new AP I plugged in, seems to have some Cat that is not punched down correctly and maybe created a feedback loop that GCP then disabled me, but I wasn’t seeing that anywhere. The only other possibility would be a duplicate IP, but it was a brand new device, so double that. Rebuilding my network last night using local software, I found the wiring issue. Unplugged the device and today my GCP instance is working. Sorry for the spam on this. Now I just need to find out how something was wired wrong in my house.

            1. I’m glad you found the culprit. I couldn’t reproduce the problem.

  99. Hi and thank you for all the work on this setup. I’ve had everything up and running for over 7 months and no issue. I have noticed in the past month that when I go to connect I get the following:

    “UniFi Controller is starting up…”

    I can stop the VM and check the logs and don’t see any reason why this it at a start up area. I can stop the VM and start it backup and it will work for about a week or so and stop again. This is just for my personal use with 3 AP and switch. Any help is greatly helpful

    1. No idea, I haven’t seen it or received any other reports. I suggest you create a new VM, assign the same IP address to it and restore the latest backup. Report back if the problem still persists.

  100. Any reason why i keep getting permission denied when i try to access /usr/lib/unifi/data .

    1. When you connect by SSH through the GCP Console you are not root. You need to use sudo for elevated privileges. If you need a root shell use sudo su -

      1. Thanks ! Now i have one more issue. It seems when I enable GuestMode that creates the portal it re-directs the guest machhine to the IP address of the controller and not the DNS hostname. I checked the cert and i see its valid only for the dns hostname and not the public ip address. I have tried to updated the controller settings to use the DNS hostname and checked the box to overide but sitll didnt help

        1. Over here we don’t tend to use captive portals. They provide little value and are expensive to maintain (like you just learned). I don’t have much experience with them – I’ve played with them, but that’s not real life experience.
          No, you can’t get a certificate for a bare IP address.
          I checked and there is a “Redirect using hostname” option in Settings > Guest Control > Guest Policies. Is that what you have tried? If that doesn’t work I’d ask on Ubiquiti forums. There is plenty of expertise there. (Ubiquiti just messed up their forum platform, but that’s an another matter)

  101. Hi!
    I got an email from google regarding a change they will do in September.
    Is this something that will affect the controller setup?

    Text from email:

    We’re writing to let you know about a change to the behavior of HTTP(S) Load Balancers that is being rolled out gradually, beginning September 30, 2019. We are making this change to standardize HTTP(S) load balancer behavior across HTTP/1.1 and more modern protocols, including HTTP/2 and QUIC.

    What do I need to know?
    After September 30, HTTP(S) Load Balancers will convert HTTP/1.1 header names to lowercase in the request and response directions; header values will not be affected.

    As header names are case-insensitive, this change will not affect clients and servers that follow the HTTP/1.1 specification (including all popular web browsers and open source servers). Similarly, as HTTP/2 and QUIC protocols already require lowercase header names, traffic arriving at load balancers over these protocols will not be affected. However, we recommend testing projects that use custom clients or servers prior to the rollout to ensure minimal impact.


    1. Have you set up a load balancer in front of you controller? In theory you could run multiple controllers if you run a separate MongoDB instance and configure the controllers to use it instead of a local one. That is a pretty complicated setup. My script just sets up a single controller without a load balancer so you don’t need to worry about this change.

      1. Hi Petri,
        How many sites can I connect to a single Controller? I have my main house, a second house, and my work all with Ubiquiti equipment. A total of 12 Access points at all three locations. (three separate sites: Home 4 Ap’s, Second House 2 Ap’s, Work 6 Ap’s) I currently run my main house and the second home on the same controller (2 sites). Can the GCP handle all three sites with one controller?

        1. AFAIK the number of sites doesn’t matter, it is only the number of devices. Ubiquiti says the first CloudKey supports up to 30 devices. The free tier VM is of similar capacity, so 12 devices should fit well.

  102. Thank you for the answer!

    I have just followed your setup and have one controller.
    In the mail from google it says:
    “Your project(s) listed below have used HTTP(S) Load Balancing in the past 60 days and may be affected by this change:”

    So I have used load balancing… but don’t know why.

    1. Hmm.. I don’t know. But anyways, the change is only in the character case of the headers. I can’t imagine how it could affect the controller.

  103. Many thanks for this, up and running quickly and all looks good. But looks like the free GCP will end in 12 months?

    1. Your free $300 credit will expire after 12 months. One micro sized VM and 5GB of bucket storage are perpetually free AFAIK. See GCP Free tier and please report if you find some changes to this.

      1. Thanks Petri. All was working fine this morning at 6:30am (UK). But by 7am all 3 of my APs were disconnected. Using PuTTY I cannot access the APs as the password is not accepted (standard), but then at about 8am all three APs came back online (but still unable to access via PuTTY).

        I wonder if security has been compromised and someone has accessed the APs?


        1. Some hiccup somewhere, but a deliberate break-in wouldn’t be at the top of my list of suspects. You probably have a firewall (USG maybe) which denies connection attempts from the Internet. UniFi controller is passive. The devices connect to the controller to report activity and get the latest configuration. The controller can’t connect to the devices because of the firewall. The attack vector would be to break into the controller, change the configuration for the devices and wait for the devices to pick up the malicious config. There is very little to gain so I haven’t heard of such an attack.
          To SSH into the devices you need to use the credentials in Settings > Site > Device Authentication.

          1. Thanks Petri, as you say a hiccup. I’ve applied new AP credentials via Device Authentication, and all looking good..

  104. Petri, I am noticing with my controller that it periodically goes offline so to speak. I get websocket errors and then my browser can’t connect to it. after about 5/10mins it comes back and all is fine, this happens a few times a day, not when backups are happening according to the schedule. do you think this is database compact / maintenance and the VM is a temp overloaded a little ? I haven’t checked in GCP console when its happening, but not sure what to look for to tie it down. I have exactly 30 devices reporting into the controller. do you think this would be signs that I am reaching the limit. no captive portal or anything running just basic stuff

    1. It could be. The 30 device limit is just my hunch based on the 1st gen Cloud Key performance. Do you see any spikes in the GCP CE VM Details Monitoring graphs? There are graphs for CPU, network and disk utilization. If you see simultaneous spikes on several graphs you could try to figure what’s going on.
      Do the graphs in UniFi Controller have breaks at these times? That would mean that the devices can’t reach the controller either.
      Another guess could be that the VM hosts are starved because Google is oversubscribing them for non-paying customers. That wouldn’t show up as your CPU utilization. You could run a next bigger instance for a few days (it won’t cost more than a dollar) and see if the problem goes away.
      Please report back if you discover something.

      1. noticed a significant performance increase when I upped the VM power, obviously to be expected, but lots of small issues with the controller have gone away. I would suspect the device limit should be around 15 almost. I noticed issues a while back, but did not actually count my devices until now, increasing them over time.

        I would have adoption errors with devices, they would fail the adopt and upgrade as I expect that puts more strain on the controller. some devices would go offline until their switch port was PoE cycled etc, then I would have the issues I described earlier with the web interface going offline. I checked the logs and cant see any gaps in the graphs so I think the controller was staying online but something was going with the Web UI. I changed the type to n1-standard-1 (1 vCPU, 3.75 GB memory), I still have the full allotment of credits from the account creation so its eating into that a little, I will run it for a week or so and see how I get on.

        Thanks for your time Petri

  105. Hi Petri.

    I have an email billing alert that I have exceeded 50% of the monthly budget.. Looking at the GCP interface I see I am in Zone us-east4-c, so I’m not sure this is one of the freebee zones? There is an overutilized warning and a recommendation to switch to a g1-small.

    Your advice appreciated..


    1. If I have understood correctly all the U.S. zones are included (except for one that is for government use only). The over utilization warning is normal. You should look into your billing statement to see what is incurring charges.

  106. I wonder if you can help Petri?I turned off both of my WiFi networks to get my son off his Xbox. I come back to my wired PC and no internet access. I can turn the WiFi back on with the Unifi Android app, but still nothing, no WiFi and no internet access. I don’t even seem to be able to ping my router! Any ideas?

    1. No idea. You should ask on the Ubiquiti forums. They will need more details though, like:
      How can the Android device connect when there is no Wi-Fi?
      Does your PC Ethernet show link light at the PC end or at the switch end?
      Does your PC get an IP address?
      If you have turned the Wi-Fi back on, can you see it on a fresh device, which hasn’t ever connected to the network?

      1. I can only use mobile data to access your website and Unifi app etc… I’ll check the forums…

        Thanks, Tony

        1. I’m still wondering what happened. How did you “turn off” the networks? There is a “Enable” checkbox for each wireless network just for this purpose.

  107. Petri,

    Thanks for this. It works a treat for both of my sites.

    Going to upgrade to two USGs so it’ll be interesting to see them in there too.

    Super job.


  108. Hey Petri,

    Thanks for putting this together. I’ve been using it through google cloud for a little over a month. A few days ago I started having issues, it may be something you’re unable to help with but thought I’d check here first.

    I have had trouble logging in, the controller hasn’t accepted my password. After 3-4 failed log in attempts I am unable to load the site at all. Chrome returns a Site Cannot Be Reached error saying the IP took too long.

    After stopping the instance and starting it again, I was able to access the login screen. Same issue though, after 3-4 failed attempts I am unable to access the site at all. On the latest restart I am unable to access the login screen at all. The logs return the following:

    Any thoughts?

    unifi-controller login: Jul 9 21:21:32 unifi-controller dbus[464]: [system] Activating via systemd: service name=’org.freedesktop.timedate1′ unit=’dbus-org.freedesktop.timedate1.service’
    Jul 9 21:21:32 unifi-controller systemd[1]: Starting Time & Date Service…
    Jul 9 21:21:32 unifi-controller dbus[464]: [system] Successfully activated service ‘org.freedesktop.timedate1’
    Jul 9 21:21:32 unifi-controller systemd[1]: Started Time & Date Service.
    Jul 9 21:21:32 unifi-controller startup-script: INFO startup-script-url: Localtime set to US/Central
    Jul 9 21:21:32 unifi-controller systemd[1]: Stopping System Logging Service…
    Jul 9 21:21:32 unifi-controller systemd[1]: Stopped System Logging Service.
    Jul 9 21:21:32 unifi-controller systemd[1]: Starting System Logging Service…
    Jul 9 21:21:32 unifi-controller systemd[1]: Started System Logging Service.
    Jul 9 21:21:32 unifi-controller startup-script: INFO startup-script-url: Return code 0.
    Jul 9 21:21:32 unifi-controller startup-script: INFO Finished running startup scripts.
    Jul 9 21:21:32 unifi-controller systemd[1]: Started Google Compute Engine Startup Scripts.
    Jul 9 21:21:44 unifi-controller unifi.init[651]: Starting Ubiquiti UniFi Controller: unifi.
    Jul 9 21:21:44 unifi-controller systemd[1]: Started unifi.
    Jul 9 21:21:44 unifi-controller systemd[1]: Reached target Multi-User System.
    Jul 9 21:21:44 unifi-controller systemd[1]: Reached target Graphical Interface.
    Jul 9 21:21:44 unifi-controller systemd[1]: Starting Update UTMP about System Runlevel Changes…
    Jul 9 21:21:44 unifi-controller systemd[1]: Started Update UTMP about System Runlevel Changes.
    Jul 9 21:21:44 unifi-controller systemd[1]: Startup finished in 1.702s (kernel) + 22.736s (userspace) = 24.438s.
    Jul 9 21:36:55 unifi-controller systemd[1]: Starting Cleanup of Temporary Directories…
    Jul 9 21:36:56 unifi-controller systemd[1]: Started Cleanup of Temporary Directories.
    Jul 9 21:48:09 unifi-controller dhclient[587]: DHCPREQUEST of on eth0 to port 67
    Jul 9 21:48:09 unifi-controller dhclient[587]: DHCPACK of from
    Jul 9 21:48:09 unifi-controller systemd[1]: Stopping System Logging Service…
    Jul 9 21:48:09 unifi-controller systemd[1]: Stopped System Logging Service.
    Jul 9 21:48:09 unifi-controller systemd[1]: Starting System Logging Service…
    Jul 9 21:48:09 unifi-controller dhclient[587]: bound to — renewal in 1596 seconds.
    Jul 9 21:48:09 unifi-controller systemd[1]: Started System Logging Service.

    1. After three failed login attempts Fail2Ban will ban your IP address for an hour. That could be one explanation. To see if your IP is banned you need to log in via SSH through the GCP Console and issue command sudo zgrep Ban fail2ban.log* You can find out your external IP address from or some other service.
      I can’t say why the three first attempts fail. Like always, it is usually not worthwhile to debug a VM problem. Just create a new VM with the same IP address and restore your latest backup from Google Storage. Of course, if the problem is within the UniFi database, it will carry over with the backup. In that case, create another VM and restore an older backup. That’s what they are for.

  109. Petri, just want to say, had my first GCP controller die today, not sure why it just stopped responding, rebooted and it just sat at controller is loading (corrupt db?). anyway I blew it away and had a new one up with your script in about 45seconds, crazy easy! thank you! leads to me to wonder if theirs a way (not free) to have redundancy with this? a second controller that mirrors the first that you could just assign the external IP too in the event of an issue ? I restored one of the backups on a docker unifi I spun up quick to make sure everything was still there, but a DNS flip to point my hostname to another IP can take like 24ish hours, so quite a bit of time to loose control of multiple sites

    1. Ubiquiti doesn’t provide any ready made high availability option. Each controller hosts its own MongoDB. Ubiquiti has documented how to use a separate MongoDB instance instead. You could build your own HA controller with two controllers using the same MongoDB database. This wouldn’t protect against database failures. You would need to use two database servers for redundancy. You could run them on the same computers as the controllers, so you would need only two VMs. Perhaps you can find someone who has already done all this.
      However, I suspect that some controller bug screwed up your data inside the database. I have a lot of faith in MongoDB. The only remedy against such bugs are backups. A HA system won’t help, since the data inside is corrupt.

  110. You’re an absolute legend, thanks for this – got me up and running in <1 hour, including getting my GCP account re-configured, DNS provisioned, etc. Very happy! Thank you…

  111. Just for clarification, in the metadata do you put your bucket name with or without “gs://” in the field? Thanks!

    1. Just like in the table in the post and on the video, just the plain bucket name without gs://. The startup-script-url requires the gs:// but it is mandated by Google – I can’t change that. I find it useless to require users to type boilerplate text. It can only cause errors, I can’t see any advantages.

  112. Is it normal for Google to halt the VM when it comes to the end of the first free year? My understanding is that $300 free for a year is part one of the deal, the other is we can use the smallest VM for free forever, but I checked my conroller yesterday and found it was offline. Went to the console to check and had to agree to any charges before I could restart the VM. Looking again the costings page is still $0 so I don’t think I’m due any charges.

    1. Yes. Google will send you emails about needing to “upgrade” your account. I guess they just want to make sure you know you might be charged. Your first micro VM in the U.S. zones is still free. You could have upgraded your account beforehand so it hadn’t stopped.

  113. Hi,

    I got this email from google:

    Hello Google Cloud Customer,

    We’re writing to let you know about upcoming changes to our pricing for external IP addresses for Virtual Machines (VM) and Cloud Network Address Translation (NAT) that will take effect January 1, 2020.

    Cloud NAT is our network address translation service that allows your applications to access the internet while keeping your deployments private, promoting the enterprise security best practice of using private VMs.

    What do I need to know?
    First, we’re increasing the price for Google Compute Engine (GCE) VMs that use external IP addresses. Beginning January 1, 2020, a standard GCE instance using an external IP address will cost an additional $0.004/hr and a preemptible GCE instance using an external IP address will cost an additional $0.002/hr.

    Second, we’re reducing the price for Cloud NAT. Beginning January 1, 2020, Cloud NAT gateway will cost $0.0014/hr per GCE instance up to a maximum of $0.044/hr and Cloud NAT data processing charges will be fixed at $0.045/GB for all regions.

    Below is a summary of the pricing changes:

    Today January 1, 2020
    External IP – Standard VM $0 $0.004/hr
    External IP – Preemptible VM $0 $0.002/hr
    Unused External Static IP $0.01/hr $0.01/hr (unchanged)
    Cloud NAT Gateway Depends on region $0.0014/VM/hr
    $0.044/hr maximum
    for all regions
    Cloud NAT Data Processing Depends on region $0.045/GB for all regions
    We will fully discount any external IP usage for the first 3 months to help you quantify the impact of these pricing changes. Please take note of the following dates:

    January 1, 2020: Although your invoice will show your calculated external IP-related charges, these will be fully discounted and you will not need to pay these.
    April 1, 2020: You will need to pay for any incurred external IP-related charges shown on your invoice.
    What do I need to do?
    The new pricing and discounts will be automatically applied to your accounts from January 1, 2020; no action is required on your part. However, we recommend migrating to private VMs for better security.

    We are committed to providing the best enterprise security tools to our customers, including Cloud IAP (Identity Aware Proxy) for administering private GCE instances from the internet, as well as Cloud NAT for secure access to public hosts. For information on how to migrate to private VMs, please see the below guides:

    For private VMs: Building Internet Connectivity for Private VMs
    For Google Kubernetes Engine private clusters: Setting up a private cluster
    If you have any questions or require assistance, please contact Google Cloud Billing Support.

    Thank you for being a valued GCP customer.

    Does this means we gonna have to pay for our external ip’s too?

    1. Yes, I got it too. Looks like the free offer is over. My math says it will cost USD4/month or USD47/year. You would pay the price of the first gen Cloud Key in less than two years. The CK still sucks, IMO. I’ll still wait til Q1/2020 to see what the charge will be.

            1. No, but apparently they will start charging for the public IP address (which you need to use it as a controller).

              1. Hi Petri,

                Firstly, please continue your work, even if that means porting your script to a self-hosted VM. The combination of SSL, Fail2ban and backup options is amazing, I’d love to continue to have the same functionality under Ubuntu Server or whatever you suggest.

                Secondly, it seems the IP for the free tier will remain free, providing the correct association is maintained. A Google employee (a googler) has said:
                “Hi Folks,

                Googler here.

                You will not be charged for your free tier VM’s external IP address.

                From the docs:

                “Your Always Free f1-micro instance limit is by time, not by instance. Each month, eligible use of all of your f1-micro instances and associated external IP addresses are free until you have used a number of hours equal to the total hours in the current month. Usage calculations are combined across the supported regions.””


                1. Thank you for your input. The response you got appears to contradict some others. I hope yours in correct. At least it very clearly answers the question we are having.

      1. Perhaps Google will edit that section, I don’t know. All current users received an email about the change to come. I’m going to wait until Q1/2020 to see what the effect will be.

        1. I messaged GCP support about it, this is the response I received:


          Thank you for reaching Google Cloud Platform Billing Support. As I understood your concern, you want clarification regarding the received email for pricing changes on January 1, 2020 and wants to know if this changes will have you reach more than $300 every year. I will be more than happy to assist you on this matter.

          Please be advised that the update was to inform you that Static and ephemeral IP addresses in use on standard VM instances will be charged $0.004 per hour starting January 1st, 2020 [1]:

          This means that an external IP address will be no longer free, even if it is in use and you will have to pay ~2.90 USD per month for each IP(v4) address.”

          So yes it appears the free IP address with a micro instance is a thing of the past.


          1. Thank you for confirming this. I added the NAT gateway ($0.0015/h) to the price so I ended up with $4/month.

                  1. I haven’t tried how Google controls it. Perhaps you will need to create a new email address. You can use the same credit card for multiple accounts, that I do know.

  114. Hi Petri,
    Thanks for detailed updated instructions.
    I have two problems.
    1. In the controller Settings – Guest Control – Enable Guest Portal Message: CONTROLLER ON-LINE REQUIRED (but guest portal work)
    2. When I open in browser – all looks fine, cert valid. But when the user try to connect to Guest Portal (Use Secure portal – Enabled, Redirect using hostname – Enabled) – Chrome and Edge show cert error. But Mozilla just open Guest Portal.
    I`m try to check cert at and seems that my unifi controller don`t have middle and root cert installed: “Certificate Chain Complete? A valid Root CA Certificate could not be located, the certificate will likely display browser warnings.”

    1. 1) I have very limited experience with the guest portal as I haven’t seen anyone use it here in Finland. Guest portals are strongly disliked over here. Perhaps you should ask on Ubiquiti forum about the check whether the controller is on-line, how is it determined.
      2) I checked my controller with the GeoCerts checker and got the same result. Too bad SSLabs checker won’t test any other port than 443. However, when I check the cert in my browsers (clicking the lock icon) I can see a full path. I use Chrome, Safari and Firefox (in that order of preference). Do you see a difference with different browsers connecting to the controller vs. the guest portal?
      I am interested in this as this may reveal some error in my certificate handling. There is an error when importing the cert chain but I have dismissed it as superfluous as I haven’t seen any symptoms. ATM I am not certain wether the problem lies in the certificate chain or in GeoCerts’ checker.

    1. AFAIK. That’s why I wrote my script for GCP.
      Personally I believe it was an attempt to gain market share for Google. A very expensive since they gathered all the tiny players who wouldn’t be paying anyways. Of course some of my script users have upgraded to a bigger VM, too. Still, I believe it wasn’t well thought out move. I was glad to make use of it, though. Google can afford it.

  115. Hi Petri,

    Due to the new changes in GCP. What is the best method and or how to move the settings and etc to my controller at home.
    I will be running a VM with ubuntu installed.


    1. Ian above received a note clearly stating that the free tier will still be free. I am waiting to see what the final verdict will be. Q1/2020 will still be free but we will see the cost accrual.

      You can run UniFi controller on a vanilla Ubuntu or Debian system. Ubiquiti has decent documentation for it. You wouldn’t need Fail2Ban on your home network nor swap or HAVEGEd on your own system. Getting a working certificate for SSL will be a struggle, but there is nothing I can do to help it with scripting. I wrote my script with the peculiarities of a cloud environment in mind and to secure a system facing the public Internet.

  116. Hi Petri,

    I saw that there is a new version of the unifi controller available.
    I thought that the script would automatically upgrade the controller from to 5.11.39 ?
    Thank you.

    1. Mine just did. The upgrade will happen the night after Ubiquiti updates the repos. They first make it available for manual downloads to get more feedback. 5.11 turned out to be troublesome for many so Ubiquiti withheld the release for a while. I am certain you either wouldn’t want your working controller to auto upgrade to a defunct version.

  117. I already have another VM besides the dedicated one for Unifi, I’m wondering if we could use GCP’s Cloud Identity and Access Management (IAM) to run the Unifi controller without a public IP to work around the static public IP charges that may come about?

    1. I can’t think of a way to use IAM, but that’s just me. I would set up port forwarding on the VM with an IP and forward ports 8443, 8080 and whatever else you need to the UniFi controller.

    1. The script were, but UniFi Controller isn’t. Java 8 and MongoDB are required, but not supported on Buster. There are workarounds, but I prefer not to kludge together something unsupported. Stretch will be supported until 2022 so I have decided to wait for true support. (Oh boy, if only Ubiquiti would move to current Java versions!)

  118. Hi Petri!

    I am trying to establish some security here by only allowing traffic from fixed ip address to the controller. The question is about ports 80 and 443. Are these for certbot? Does letsencrypt need open access to the webserver? Lighttpd is only for redirecting, right?

    too many questions in one comment, I know…

    thank you for your support!

    1. Yes, Let’s Encrypt uses either 80 or 443. Lighttpd is only for redirecting. Lighttpd is stopped during certification renewal when CertBot takes over those ports.

  119. Seems my controller quit working 2 days ago, and I only realised today. Going into console it confirmed the free trial had ended, and only allowed the VM to be started after I emailed billing. Has anything changed that you’re aware of?

    1. After your first year credit expires you need to confirm/update your account. I believe this is so you are made aware of the forthcoming charges. I dismissed my email notices and got my account locked up at the time. All it took was a few clicks in the billing section.

      1. Yes I seem to have got it back up and running… Bit surprised there was no notification and they just stopped the VM! Question is… How do you estimate the cost? I’ll need to weigh against just migrating to a cloud key.

        1. The notification email is probably in your spam folder.
          The first U.S. based micro sized VM is free. This apparently includes the new charge for the IP address as well. Something Google didn’t make clear at first and it caused a lot of confusion.

          1. I couldn’t find the notification anywhere, but nevermind it for now.

            I’ve noticed that I’ve started to accumulate ‘real’ costs now for the traffic between US and UK. I’m monitoring and will see how it works out over a full month.

            Should I decide to migrate to a local cloud key, what’s the process? Do I just download a backup from the VM and restore that onto a local controller?

            1. My network is in Finland but I haven’t been charged. What do carry over the Atlantic? My VM and backup bucket are in the same zone in the U.S. Inbound traffic is free. Outbound (from U.S. to U.K.) is charged after a threshold. That contains you browsing the controller and firmware upgrades from the controller to the devices. My traffic has been well below the threshold.
              Yes, you just restore the latest backup to the new controller (CloudKey, local PC, some other cloud VM). Then you change the inform address in the old controller to point to the new.

              1. Not entirely sure, but it appears only 1gb of egress is free monthly, the rest at a charge. My average over the last several months is about 4gb. What does yours look like out of curiosity?

                1. 1Gb should be plenty! I just went through one site’s billing information. One month this year there was 2Gb of transfers (in March) but I couldn’t get the details for but the last three months. Nobody knew anything specific had happened in March so it is a mystery. There were some small amounts of traffic to&from APAC for example, which I can’t explain. My guess is they are some exploit attempts.

                  1. I’ve no idea what can be using that kind of bandwidth. I have only A USG, two AP’s, and two switches. All of this hosts 40-50 network devices. From what you’ve said it’s only firmware updates and (presumably) accessing the controller via browser or app.

                    Any way to tell what’s using this?

                    1. I believe it would be wasted effort. For example, you may have an IP that was used for some other service earlier and b/c of some misconfiguration there are millions of devices that keep trying.
                      Create a new VM, grab a new IP address, point your DNS to the new IP, restore the latest backup and point your devices to the new inform host.

  120. Hi there,
    Thanks for your assistance with this.

    I got an e-mail from Google stating the following:
    E-mail Subject: “[Action Required]” Legacy GCE Metadata Server endpoints will be shut down on January 15, 2020″

    Hello Google Compute Engine Customer,

    We are writing to let you know that the v0.1 and v1beta1 endpoints of the Compute Engine Metadata Server will be shut down on January 15, 2020. From January 15, 2020, requests to the v0.1 and v1beta1 endpoints will no longer be supported, and may return HTTP 404 NOT FOUND responses.

    Our records show that you own projects that have requests to these endpoints. You will need to update your requests to the v1 endpoint before January 15, 2020….

    I’ve tried to read up on this but Google’s documentation does not seem to be compatible with my brain. Can you assist?

    1. Hmm.. I haven’t seen that announcement. However, it only says that when the script reads metadata it has to provide a Metadata-Flavor header. My script has always included it as far as I can remember. Perhaps you have used some other script or made some personal experiments with the metadata? Anyways, the script only runs at boot so this change won’t affect running controllers. Even if the script would fail during boot the controller will still start (without any metadata changes, though).

  121. Hi Petri,
    Thanks for all of the work you have put into this!
    I have followed all instructions and have a fully functioning cloud controller. It is a beautiful thing!
    I have one problem/question/request???
    I am not sure how to view the log files I believe they are located at /usr/lib/unifi/logs/
    I there any way that the log files for the controller could be exported to the bucket so that they can be reviewed?
    Could this be part of the script?

    1. I wrote the script for people who don’t know Unix and really don’t care. They just want their Wi-Fi to work. They never look at their Windows events either. Copying the logs to a bucket would just waste my time, storage capacity and transfer bandwidth.

      If you are savvy enough you can log on using the SSH button in the console. Use sudo su - to get a root prompt to get to most of the UniFi stuff. If you really want you can duplicate and edit /etc/systemd/system/unifi-backup.service and /etc/systemd/system/unifi-backup.timer to copy the logs.

  122. Hi Petri,
    Great news on the f-1 micro sized VM staying free (fingers crossed). I am curious on how the “Always Free” usage limits are calculated. What does 28 frontend instance hours per day mean and 1GB of egress per day mean?
    I like to log onto my unifi controller a few times a day from both my computer and the App (I’m sure this will subside once the newness of it all wears off).
    Am I at risk of busting the limits if all I do is log on a few times a day?

    1. No worries. I’ve kept a browser window open for months without any significant traffic. The only times you cause significant traffic is when you download backups to your local computer (easy to track) and when your devices download firmware updates from the controller (by default manual operation as well). Each device downloads its firmware separately, so there is a risk of excess traffic if you have many devices.

  123. Does anyone else get alerts each day saying their USG / APs were disconnected? I seem to be getting this. Its around 4am which is when I have my daily backup set up? Could the backups be taking up too much server resources leading to the devices missing their heartbeat?

    1. I haven’t seen many of those, but yes, it could happen. The controller kind of freezes when it is compacting a database or creating a backup.

  124. Prefacing this comment by saying a huge thanks for this guide/script, works great.

    I do have an issue though where if I reboot the controller my devices don’t reconnect. When I log into my USG for some reason the FQDN of my controller is resolving as

    PING unifi ( 56(84) bytes of data.
    64 bytes from localhost ( icmp_req=1 ttl=64 time=0.338 ms
    64 bytes from localhost ( icmp_req=2 ttl=64 time=0.254 ms

    To get my devices to reconnect I have to set-inform http://GoogleExternalIP:8080/inform and they all reconnect stright away. I use my own domain so not a shared DNS service.
    Any ideas?

    1. set-inform isn’t covered in the instructions 🙂 The devices store the set-inform address and a backup value. If the primary host can’t be contacted they fall back to the backup – and never retry the primary. The solution is to issue set-inform twice to overwrite the backup value, too.

  125. There might need to be some updates to the LetsEncrypt section. I’m running it today and getting an error

    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Plugins selected: Authenticator standalone, Installer None
    Registering without email!
    An unexpected error occurred:
    The client lacks sufficient authorization :: Account creation on ACMEv1 is disabled. Please upgrade your ACME client to a version that supports ACMEv2 / RFC 8555. See for details.
    Please see the logfiles in /var/log/letsencrypt for more details.

    I found this article that includes today as a brownout day.

    I’m not sure what ACME client would be, since it is just using certbot.

    Any thoughts?

    1. Thanks for the heads up. I thought certbot should automatically switch to the new protocol. I’ll look into this and update this comment.

      1. You haven’t followed my instructions if you are on Ubuntu 🙂 Debian repos are at certbot 0.28. Good to know an update will solve this. I’d hate to hardcode a specific endpoint version into the script.

        1. Hah. No I’m not following the instructions to the letter. I’m adapting it for Ubuntu to get it running on Oracle.

          Apparently I should not assume that:
          1) Ubuntu repositories would install a current version of certbot
          2) Ubuntu and Debian would install the same (or close) versions

  126. Petri,

    It seems that fail2ban isn’t working in my setup. I followed the instructions & the video and in my console logs, I can see that the fail2ban service has started

    Oct 17 19:24:23 unifi-controller systemd[1]: Starting Fail2Ban Service…
    Oct 17 19:24:23 unifi-controller fail2ban-client[28798]: 2019-10-17 19:24:23,365 fail2ban.server [28799]: INFO Starting Fail2ban v0.9.6
    Oct 17 19:24:23 unifi-controller fail2ban-client[28798]: 2019-10-17 19:24:23,366 fail2ban.server [28799]: INFO Starting in daemon mode
    Oct 17 19:24:23 unifi-controller systemd[1]: Started Fail2Ban Service.
    Oct 17 19:24:23 unifi-controller systemd[1]: Reloading.
    [ 183.775165] systemd[1]: apt-daily.timer: Adding 7h 53min 36.834348s random time.
    Oct 17 19:24:23 unifi-controller systemd[1]: certbot.timer: Adding 2h 35min 2.601576s random time.
    Oct 17 19:24:23 unifi-controller systemd[1]: apt-daily-upgrade.timer: Adding 26min 38.884724s random time.
    Oct 17 19:24:25 unifi-controller startup-script: INFO startup-script-url: Fail2Ban installed
    Oct 17 19:24:25 unifi-controller systemd[1]: Reloading Fail2Ban Service.
    Oct 17 19:24:26 unifi-controller systemd[1]: Reloaded Fail2Ban Service.

    Since then, I’ve tried to fail my own logon on a different network, and I still get the landing page even after the 3 bad attempts. When I run the sudo zgrep Ban fail2ban.log* command in the /var/log directory, I don’t get any returns for the blocked IP’s. Is there something I’m missing in the configuration or otherwise? Everything else is working as intended from the cert to the ddns.

    1. You seem to have spotted something interesting. UniFi controller doesn’t log failed logins any longer. Fail2Ban runs just fine, but there is nothing in the log! You need to change the logging level in the Controller: Settings > Maintenance > Services > Log level > Mgmt > More > Apply. I have updated the blog post step 5 accordingly. Thank you!

  127. Awesome, thank you so much Petri. That fixed it no problem.

    Now to figure out why my AP’s don’t migrate to the GCP controller, when the USG and switches migrated no problem. If I ssh to an AP, it says it’s unable to resolve my inform address, or any other address, I can ping my static GCP IP without issue. I’m assuming this is because the USG is no longer on the same controller providing DNS. I’m going to try and bring the USG back into the original controller and then use the unifi migration tool.

    1. Even if the USG is not on the same controller, it is still providing DNS for the subnet. The APs get the DNS setup via DHCP. It is completely independent from the controller.
      How about if you use the IP address for the inform host? Will they connect? There is no added value in using a DNS name as long as you have the same IP address. You could change the inform host from the GCP controller after the migration, if you want.

  128. Hi! I have had the controller running for over and year and just noticed that Google charged me $0.02 for the month of October and the changed was for:
    Unifi Controller studious-linker-219118 $0.34 -$0.31 — $0.03

    What do you think is causing me to be charged. It’s not a large amount I know, but just strange that all of the sudden I’m being charges.

    Thanks for you help!

    1. I really can’t tell. One explanation could be a common script trying to exploit some PHP vulnerability and sending zillions on http requests. You controller would always send back the login page. All those login pages could eventually add up and go over the free egress amount. There is very little we can do about that. You could change your IP, but that could case an outage before DNS settles.
      The way my script is written those http requests will always be redirected to the UniFi controller and not logged.

      1. I also got charged for 0,02 euro. Never had this before…
        So maybe you need to change something inside your script.

        Because i have nothing else running besides your basic script.

        1. Actually the script is not running. The script only runs once at boot, checks the setup and exits.
          I am thinking if there is something I could do to help this. I’ll announce it here if I come up with something.

  129. Hi also getting some charges. Storage PD Snapshot in US, Network Internet Standard Tier Egress from South Carolina.

    Each for .04 USD. Any ideas? I though some storage was free.

    1. I am working on a solution. Apparently there are tons of robots downloading the login page causing the traffic. Please check back next week at latest, I hope.

      1. Hi Petri any update on the script fix to stop the charges from Google? I can see a lot of Egress to Japan, Mumbia and other areas. I believe this is was causing the charges to show up.

        1. Not yet. I have apparently found a bug in Fail2Ban, which needs to be fixed. In the meantime, you can disable Lighttpd altogether by logging in and issuing sudo systemctl stop lighttpd and sudo systemctl disable lighttpd. That means you will need to use the full https://my.ctrl.dns:8443 URL, but your browser should already know it.

  130. Do you have an issue where the WiFi drops for a minute after the controller restarts at 4:00 AM?

    1. No, that shouldn’t happen. The devices don’t depend on the controller. They can work indefinitely without one. You can run the controller on a laptop and only launch it when you want to change the configuration. The cause is something else.
      The controller doesn’t restart every night. Only when there are kernel patches or something alike that requires a reboot. Debian development has moved to Buster and later, so our Stretches see very few patches.

      1. I restarted the controller and it didn’t have the WiFi disconnect so I think maybe the internet drops at 4 AM normally or the garage door opener is broken and disconnects from WiFi and then complains that it’s offline every morning.

  131. Hi, Curious how long it typically takes for the script to install the latest version of the controller. Looks like I am on Version 11.50 but there is 12.22 on the site. Thanks.

    1. My script won’t install any updates. My script only does the initial setup. After that, unattended-upgrades service takes care of updating. It runs every night and will install the latest version as soon as Ubiquiti releases it to the repository. This has usually taken about a week after the release. With such a big version bump this time they may want to gather more feedback from the early adopters. My script is not for them. I wrote it for people who don’t want to fiddle with their system.
      There is no easy way to downgrade a controller. You only want to upgrade when the upgrade has been proven to be solid.

  132. Hi Petri,
    Apologies if this is already answered above; Ingot an email from Google stating “Action requested: Please take a moment to upgrade your account (expiring in 7 days).”

    It says if I don’t upgrade, my VM will stop working. If I do upgrade, do I get charged for the f-1micro?

    Very confusing information from Google.


    1. That just means that you first year’s credits are gone. Google just wants to double check that you are ready to pay if you exceed the free tier. The first f1.micro is still free.
      You need to comply. I dismissed the notice and got locked out from my account at the time. No need to retry that.

      1. Ok, I upgraded (hopefully it stays free).

        Question: I looked at my dashboard for billing, and it appears there is a small charge every month for “Cloud Key Management Service (KMS) ” It’s a different amount every month. What is it, do I need it, and can I turn it off?


        1. No idea. I haven’t seen that. Turn it off if you can find where. If something breaks, report here.

          1. Do you have any more news on the 0.03 cent that we had to pay extra this month regarding the http traffic? You said you would come with a solution the latest by the end of this week…

            1. I have found out that there are huge bot networks combing through GCP IP addresses looking for common web server vulnerabilities. They all get the login page, which will add up to the egress traffic. I have tested a remedy against the bots, but it requires changes in the setup. I haven’t yet found a safe way to implement the changes to existing controllers. It looks like you will need to create a new controller. Don’t do this yet, since I haven’t published the new version. I’ll let you know when I am confident with it. Keeping the controller functional is more important than a dime, after all.

              1. Hello Petri,
                Thank you so much for your work!

                Just my 2 cents about the unwanted traffic discussion.

                I expanded your firewall rules with specific source IPs.
                For every site I add, I first add the IP. Since I cannot protect this controller with 2FA, I felt uncomfortable leaving it like it was. In a way we need a private cloud for the component.

                I have to leave port 80 open for Let’s Encrypt auto-renewal to work which visits the well_known folder, but at the root only the redirect exist. All the other ports only allow traffic from the different sites running equipment.

                It does mean I can only log into the controller from one of those IP’s, but with a VPN connection that is not that much of a trouble, even from phones/tablets.

                To keep sane, I had to change the composition of the firewall rules a bit in G-cloudconsole.
                -I have one rule for every site containing all the ports without 22.
                -I use the original letsencrypt rule port 80 since I cannot predict the origin of letsencrypt validation calls. (Side-note: validation via DNS could be possible if txt record creation can be automatically published in a DNS-zone.)
                -I have a separate SSH rule port 22 rule for when I need it, since SSH terminal started from G-cloudconsole can come from different IPs.

                Since I don’t add new Controller site every week or month, I accept the inconvenience of the firewall rules in exchange for not having unwanted log-in attempts. When I set it up, it was because of security, but nowadays I found an extra argument: no weird traffic that costs money.

                Curious to see what you have been doing, look forward to reading it.
                Possibly the case above can be of aid to some. (If it passed already; sorry for missing it.)


                P.s. I read you FAIL2BAN remark at the top. Do you have a mailing list? Updates that break a security measure like this would be something I would like to know asap. I know you do this all for nothing, don’t get me wrong, I am very grateful. But if there are any existing ways to stay in the loop, let me know.

                1. Hello and thank you for your input. I wrote the script and instructions for users who have zero experience with Linux or cloud services. Many users have reported success with very little IT skills in general. Apparently you could have done it all without my script. Setting up firewall rules for specific IP addresses and ports is way beyond my original scope. My intention was to make the setup as secure as possible but keep it as a turn key. That’s why there are automatic upgrades and Fail2Ban rules.

                  There is no mailing list at the moment. This Ubiquiti’s change of logging has been the first to actually break something. I put up the notification at the top so you don’t need to wade through all the comments to see if something important has turned up. I never expected this to become so popular. I’ll need to think about this.

          2. Hi Petri,
            somehow the Cloud Key Management Service (KMS) API was enabled on my GCP. I want to disbale it, however it says “If any resources were created by Cloud Key Management Service (KMS) API, they may be deleted soon after Cloud Key Management Service (KMS) API is disabled.
            All code that uses this project’s credentials to call Cloud Key Management Service (KMS) API will fail.”

            My concern is; does this have anything to do with my LetsEncrypt Cert? If not, I will disable. Thanks

            1. I don’t have that enabled. No, it doesn’t have anything to do with Let’s Encrypt, either.

  133. My controller is still on and keeps telling me “Controller software update 5.11.50 is now available.”. I’ve done multiple reboots and it never seems to update. Any idea how I troubleshoot this?

    1. It should have upgraded by now. If you want to troubleshoot it, login by SSH and look in the logs in /var/log/unattended-upgrades. You may want to try sudo apt-get update and sudo apt-get upgrade to see any error messages. However, my recommendation is to create a new VM, transfer the same IP address to it and restore the latest backup. It is a quick procedure and if a VM has misbehaved in the past it doesn’t bode well for the future.

      1. 2019-11-22 04:29:24,323 INFO Initial blacklisted packages:
        2019-11-22 04:29:24,338 INFO Initial whitelisted packages:
        2019-11-22 04:29:24,338 INFO Starting unattended upgrades script
        2019-11-22 04:29:24,338 INFO Allowed origins are: [‘origin=Debian,codename=stretch,label=Debian-Security’]
        2019-11-22 04:29:26,680 INFO No packages found that can be upgraded unattended and no pending auto-removals

        Nothing here really. I did apt-get and it found the update and upgraded. I’ll keep an eye on it and if it does it again with the next release I’ll create a new instance. Cheers!

        1. Yes, there is. Allowed origins should be ['origin=Debian,codename=stretch,label=Debian-Security', 'o=Debian,a=stable', 'c=ubiquiti'] according to my script. The last one is for UniFi updates. Please check if /etc/apt/apt.conf.d/51unattended-upgrades-unifi exists and what its content is.

  134. Thanks Petri for your work and the script. In the past I set up unifi controller at aws.

  135. Hi Petri,
    I’m seeing a charge for $0.22 (I know, nothing to cry about) for “Network Internet Egress from Americas to EMEA” It says 2.1GB egress was sent to EMEA from my GCP. WHat is IMEA and is there any way to stop it? Thanks.

    1. EMEA is an acronym for Europe, Middle-East and Africa. I am working on a fix, but it takes some time to test each intermediate version. That’s why it is taking so long.

  136. Hi Petri,
    You’ve helped before and we’re very grateful 🙂 We run 20 sites with 78 devices. We have it on a f1-micro (1 vCPU, 0.6 GB memory). We keep not being able to access it and have to restart it. Once it’s restarted it works fine for a while. Any idea why? Thanks!

    1. The performance of f1-micro is on par with CloudKey gen1, which suffices for up to 30 devices says Ubiquiti. You should upgrade to next larger instance (for a fee). At least you can try it for a month and see if it solves the problem.

    2. I dont want to speak for Petri, but according to him: “Ubiquiti quotes 30 devices for 1GB Cloud Key” and according to Petri, the f1-micro is similar to the cloud key. With 98 devices, you might need to bump up your VM. My $.02