SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor · Updated 2026-08-16

How to Run UniFi Controller for VPS Without Wahala

Learn how to host UniFi Network Application for VPS, size RAM, run Docker with MongoDB, use set-inform for Layer 3 adoption, and keep ports private.

Wetin UniFi controller for VPS dey really do

UniFi controller for VPS na one management server wey stay reachable even when the sites wey e manage go down. The software na Ubiquiti UniFi Network Application: Java program wey get MongoDB database behind am. E dey configure your access points and switches, store their statistics, and serve the admin interface. E no dey carry client traffic.

That last point na wetin decide where e suppose dey. If you put the controller for machine inside the office wey e dey manage, you go lose the network and the tool wey you need to check the network for the same minute. If you put am for VPS wey get stable public address, e go keep running, keep collecting data, and adopt devices for different sites from one place. Wetin e need na uptime, no be plenty processing power.

When controller dey offline, adopted access points and switches go continue to forward traffic with the configuration wey dem don already push to dem. You go lose the dashboard and statistics, plus any feature wey need the controller to dey live: guest portal login, or RADIUS (remote authentication dial-in user service) if the controller na your RADIUS server. Clients go remain connected.

UniFi controller need RAM how much?

Two GB na the minimum, and 4 GB na the amount wey make sense to buy. Two things dey use memory for one box: Java and MongoDB. Each one dey manage its own memory separately.

MEM_LIMIT dey set the limit for Java heap, and the container image set am to 1024 MB by default. MongoDB na the other part. Its WiredTiger storage engine dey set cache to half of the RAM above 1 GB, or 256 MB, whichever one bigger. For 2 GB VPS, this na about 512 MB cache plus 1 GB heap plus JVM own non-heap memory plus the operating system. E go fit work until one busy day come, then kernel out-of-memory killer go terminate one of the two processes. After any restart wey you no explain, run dmesg -T | grep -i 'killed process' to check whether na this happen. If na 2 GB you get, add swap file.

CPU and disk no demand much. One or two vCPU fit handle some dozens of devices. Start with 20 GB disk and monitor am, because database dey grow based on the number of clients wey you get and how long you keep statistics. Controller by itself go leave most of 4 GB box unused. So if you plan add another service, size the system for that service first, because PhotoPrism and Immich get very different RAM minimums and either one need more RAM than the controller.

One CPU feature matter, and e easy to overlook for cheap plan:

grep -m1 -o avx /proc/cpuinfo

MongoDB 5.0 and later need AVX (advanced vector extensions) for x86_64 hardware. If that command print nothing, mongod go die during startup and the container go restart continuously, because the binary dey run instruction wey the CPU no get. Older Intel Celeron and Pentium hosts na the usual cause. Hypervisors wey hide CPU flags from the guest fit cause am too. MongoDB 4.4 no need AVX and na the only fallback, but upstream no dey patch that database version again. Moving to host wey get newer CPU na the better solution. For ARM VPS, this question no arise, because AVX na x86 instruction set, and both images publish arm64 builds. If you dey choose between the two, differences between ARM and x86 VPS plans go beyond the price.

Install UniFi Network Application wit Docker Compose

Docker na the option wey get the fewest surprises, because e let you pin MongoDB to the version wey the application support instead of using any version wey your distribution release. If Docker never dey for the box, install Docker for VPS first.

mkdir -p ~/unifi/config ~/unifi/db
cd ~/unifi

MongoDB need one user before the application fit log in. The official MongoDB image go run any script wey e find for /docker-entrypoint-initdb.d for the first start. Save this as ~/unifi/init-mongo.sh:

#!/bin/bash
if which mongosh > /dev/null 2>&1; then
  mongo_init_bin='mongosh'
else
  mongo_init_bin='mongo'
fi
"${mongo_init_bin}" <<EOF
use ${MONGO_AUTHSOURCE}
db.auth("${MONGO_INITDB_ROOT_USERNAME}", "${MONGO_INITDB_ROOT_PASSWORD}")
db.createUser({
  user: "${MONGO_USER}",
  pwd: "${MONGO_PASS}",
  roles: [
    "clusterMonitor",
    { db: "${MONGO_DBNAME}", role: "dbOwner" },
    { db: "${MONGO_DBNAME}_stat", role: "dbOwner" },
    { db: "${MONGO_DBNAME}_audit", role: "dbOwner" },
    { db: "${MONGO_DBNAME}_restore", role: "dbOwner" }
  ]
})
EOF

That script dey run only when the database directory empty. If you start the stack once with wrong password, the user go get that wrong password. Editing the compose file afterwards no go change anything, because the script no go run again. The symptom be say application container dey log MongoDB authentication failures, while the web interface no dey appear. For fresh install, stop the stack, delete ~/unifi/db, then start am again.

Then write ~/unifi/compose.yaml:

services:
  unifi-db:
    image: docker.io/mongo:8.0
    container_name: unifi-db
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=change-this-root-password
      - MONGO_USER=unifi
      - MONGO_PASS=change-this-unifi-password
      - MONGO_DBNAME=unifi
      - MONGO_AUTHSOURCE=admin
    volumes:
      - ./db:/data/db
      - ./init-mongo.sh:/docker-entrypoint-initdb.d/init-mongo.sh:ro
    restart: unless-stopped

  unifi-network-application:
    image: lscr.io/linuxserver/unifi-network-application:10.5.67-ls141
    container_name: unifi-network-application
    depends_on:
      - unifi-db
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
      - MONGO_USER=unifi
      - MONGO_PASS=change-this-unifi-password
      - MONGO_HOST=unifi-db
      - MONGO_PORT=27017
      - MONGO_DBNAME=unifi
      - MONGO_AUTHSOURCE=admin
      - MEM_LIMIT=1024
      - MEM_STARTUP=1024
    volumes:
      - ./config:/config
    ports:
      - "8080:8080"
      - "3478:3478/udp"
      - "127.0.0.1:8443:8443"
    restart: unless-stopped

Dem pin both image tags intentionally. 10.5.67-ls141 na the current application release for August 2026, so check the image release list and pin the version wey current when you install. The database tag matter more. MongoDB no dey upgrade its data files across major versions by itself. So one day mongo:latest go pull new major version, refuse to open the files wey e find, then restart repeatedly. Pin the major version and move am deliberately. UniFi Network 8.1 and later support MongoDB 3.6 through 7.0, and 9.0 add support for MongoDB 8.0.

PUID and PGID must match real user for the host. Otherwise, the files under ./config go end up owned by identity wey no fit write dem. Run id to get your own values. how PUID and PGID dey work for container images explain wetin mismatch dey look like.

Start am and monitor the logs:

docker compose up -d
docker compose ps
docker compose logs -f unifi-network-application

docker compose ps suppose show both containers as running. If unifi-db remain stuck for restarting, na either the AVX problem above or permission problem on ./db. After the log settle, check the two listeners:

curl -sk -o /dev/null -w '%{http_code}\n' https://127.0.0.1:8443/
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/inform

Any HTTP status code at all mean say the listener dey bound and dey answer. Connection refused mean say the application still dey start. For small VPS, first run fit take one or two minutes. E fit also mean say the application never start.

Reach admin interface without exposing am

Port 8443 dey published on 127.0.0.1 for the file wey dey above, so nothing outside the VPS fit reach admin interface. Forward am through SSH to run setup wizard:

ssh -L 8443:127.0.0.1:8443 you@vps.example.com

Leave that session open and browse go https://127.0.0.1:8443. Certificate na self-signed, so browser go warn you once. Create administrator account, give the site name, and skip device adoption for now.

SSH tunnel dey okay for one administrator. For team, give the VPS private address and bind the interface to that address instead. WireGuard VPN for your own VPS and Tailscale subnet router both give you address wey only your people fit route to. Change the published port to 10.8.0.1:8443:8443 for WireGuard, or use the address wey Tailscale assign. One issue be say Docker no fit publish on address wey never exist yet, so tunnel interface must come up before container start, otherwise container go fail with bind error.

Why remote UniFi device no go adopt

When you first bring am come out, UniFi device dey find e controller by broadcasting for the local network, UDP port 10001. Broadcast no dey leave LAN, so device wey dey office for another city no go ever discover controller for VPS. Na Layer 3 adoption be this, and na here most people dey stuck. The device dey okay and the controller dey okay. Nobody don tell the device where to look.

First, tell the controller the address wey e suppose hand out. For the controller Settings, inside the System section, you go see inform host setting with override option. Set am to the public hostname or IP of your VPS. If you no set am, the controller go advertise the address wey e see for e own interface. Inside Docker bridge network, this one fit be private address like 172.18.0.3. The device receive that address, e no fit route reach am, then e go return to searching.

Then point the device to that address. SSH into the device for the remote LAN. Factory default device dey accept username ubnt with password ubnt:

ssh ubnt@192.168.1.20
set-inform http://vps.example.com:8080/inform

Newer device firmware fit put you inside menu instead of shell. Run the same thing as one command:

ssh ubnt@192.168.1.20 mca-cli-op set-inform http://vps.example.com:8080/inform

The device go now show for the controller as ready to adopt. Click Adopt, and the state go change to Adopting. Na this part dey surprise everybody: normally, you need run set-inform one more time. The device go restart into provisioning and fall back to the inform URL wey e save for e own configuration. The controller never finish replace that URL. Run the command again while the state dey read Adopting to complete the handover. Type info for the device to see the inform URL and state wey e currently hold.

If another controller don adopt the device before, set-inform alone no go complete the process because the device still hold that controller credentials. Reset am to factory default first, either with the reset button or with set-default over SSH using the old credentials.

If you get more than small number of devices, use DHCP instead. DHCP (dynamic host configuration protocol) option 43 carries vendor-specific value, and UniFi devices read the inform URL from suboption 2. Build the hex string on any Linux box:

URL="http://vps.example.com:8080/inform"
HEX=$(printf '%s' "$URL" | od -An -tx1 | tr -d ' \n')
printf '02%02x%s\n' "${#URL}" "$HEX"

For http://192.168.3.10:8080/inform, wey be 31 byte string, the command go print 021f687474703a2f2f3139322e3136382e332e31303a383038302f696e666f726d. Paste the result inside your router DHCP option 43 field as hex value. Every device wey boot for that network go learn the controller address from e lease, without any SSH. Older guides dey show suboption 1 instead, 0104 followed by the four bytes of IPv4 address for hex, and devices still accept that format.

There is third way if you dey run DNS for the site. UniFi device dey try resolve hostname unifi when e boot, so A record for unifi wey point to your VPS address go adopt devices without per-device work. E only help for places where you control the resolver wey the devices actually use.

Wetin UniFi ports to open, and which ones to keep private

Na only two ports need remote site to reach dem.

  • TCP 8080 na the inform channel, and every adopted device dey connect to am. The payload inside dey AES encrypted with a key wey the controller give the device during adoption. Na why plain HTTP dey be the normal setting for here.
  • UDP 3478 na STUN (session traversal utilities for NAT). Devices dey use am to keep path back to the controller.

Every other thing stay closed for VPS.

  • TCP 8443 na the admin interface. This one must never be public. E hold configuration for every site wey the controller dey manage, behind one password.
  • UDP 10001 and UDP 1900 na broadcast discovery. Broadcasts no dey cross internet, so opening dem no achieve anything.
  • TCP 8880 and TCP 8843 na guest portal redirects. Open dem only if you dey run guest portal.
  • TCP 6789 na mobile speed test, and UDP 5514 na remote syslog. Add dem when you dey use dem.
  • TCP 27117 na MongoDB. For the compose file above, the database publish no ports at all, so e dey only for the internal Docker network. Keep am like that.

If your sites get static public addresses, allow only those:

sudo ufw allow OpenSSH
sudo ufw allow proto tcp from 203.0.113.4 to any port 8080
sudo ufw allow proto udp from 203.0.113.4 to any port 3478
sudo ufw enable
sudo ufw status verbose

the ufw basics for a VPS firewall cover the default deny setup wey those rules dey assume.

One trap dey here wey dey catch people every time. Docker published ports dey bypass ufw. Publishing a port dey write NAT and forwarding rules directly into iptables, and na Docker own chain dey filter that traffic, not the INPUT chain wey ufw dey manage. So ufw deny 8443 fit look correct for ufw status while the port still dey open to everybody. Test am from another machine, never from the VPS itself:

nc -vz vps.example.com 8443

Na refusal or timeout you want. If e connect, the port dey public no matter wetin ufw talk. The reliable fix na the one already dey for the compose file: publish the port on 127.0.0.1 or on a tunnel address, so Docker no go bind am to the public interface. Rule for the DOCKER-USER chain fit work too, but binding dey simpler, and mistake for rule ordering no fit undo am.

Wetin about Ubiquiti own installers?

Ubiquiti dey publish Debian package for the Network Application. E dey work, but for current Ubuntu e dey raise one MongoDB matter wey the distribution no longer solve: Ubuntu 22.04 and 24.04 no dey ship MongoDB server package, so you go need add MongoDB own repository and match versions by hand. The container above dey handle this matching inside one pinned tag, na why we dey use this path here.

Ubiquiti newer self-hosted product na UniFi OS Server. E dey run UniFi applications inside Podman containers and give you the same UniFi OS wey their hardware consoles dey use. As of August 2026, e need x86_64 Ubuntu 22.04 or 24.04, Podman 4.3.1 or newer with slirp4netns, and e ask for 2 vCPU with 4 GB RAM as minimum. E recommend 4 vCPU with 8 GB. The installer dey behind free Ubiquiti account for their downloads page, so no stable one line URL dey wey you fit paste inside guide. E dey create system user called uosserver and run the containers as that user. Choose am if you want the vendor own packaging. Choose the container stack if you want pin versions by yourself and keep the server free for other work.

Where UniFi backups dey, and how to carry dem comot from the box

The controller dey write im own backups according to the schedule wey you set for Settings, for the backup section, together with how many copies to keep. The files dey land for /config/data/backup/autobackup inside the container, wey be ~/unifi/config/data/backup/autobackup for the host, with names like autobackup_10.5.67_20260813_1200_1755086400004.unf.

Check say dem really dey show:

ls -l ~/unifi/config/data/backup/autobackup

If the directory empty one day after you set schedule, na known failure for fresh container installs. The application dey expect autobackup directory to already exist, but e no create am. So the scheduled job quietly write nothing. Create am yourself with the same user wey the container dey run as, then wait for the next run:

mkdir -p ~/unifi/config/data/backup/autobackup
docker compose restart unifi-network-application

A .unf file dey hold the site configuration and administrator accounts, so treat am like cryptographic key. Pull copies go machine wey you control, and keep dem private:

rsync -av you@vps.example.com:~/unifi/config/data/backup/autobackup/ ~/unifi-backups/

Restore na one step. The first page of setup wizard for new install go offer to restore from backup file. A running controller fit also take one from the same settings page. Restore go the same version or newer version. If newer application write the backup than the one you dey restore into, e go reject am. Na why you suppose record the version number together with the file.

Wetin controller upgrade fit spoil

Make manual backup and download am before every upgrade. Then:

docker compose pull
docker compose up -d
docker compose logs -f unifi-network-application

Na database be the first thing wey fit fail. If you change the mongo tag go new major version for the same edit wey you use upgrade the application, controller fit no start. MongoDB no fit open data files from different major version unless you do staged upgrade. Upgrade the application by itself. Move MongoDB separately, one major version at a time, with fresh backup ready.

Memory na the next issue. Newer release need bigger heap. If application start, run for some minutes, then die, increase MEM_LIMIT and MEM_STARTUP to 1536 or 2048, then restart. dmesg -T | grep -i 'killed process' for the host go confirm whether na kernel end am.

Device firmware na the risk wey people dey forget. After controller upgrade itself, e go offer firmware upgrades for adopted devices. No accept dem for the same session. If device upgrade and controller upgrade happen together and the link between dem drop, device fit remain half provisioned. Then you go need set-inform over SSH on hardware wey dey another building.

The upgrade window no harsh as e sound. Devices go continue to forward traffic while controller dey restart, so users no go notice anything. But guest portal and RADIUS go stop if na controller dey serve dem. So choose time wey nobody dey use dem. If controller quietly die for 3 a.m., e good make you know. Point Uptime Kuma status monitor to port 8080 and make e alert you.

The honest alternative: Ubiquiti hosted console

Ubiquiti dey sell the same work as service. As of August 2026, Official UniFi Cloud Console start from $29 every month and e fit manage up to 500 UniFi devices, while Ubiquiti dey handle the updates and backups. The self-hosted application wey you just install na free, and e no get subscription.

Choose hosted console if na one site you dey manage and you prefer pay instead of patch. Choose VPS if you dey manage several sites, or if you want the controller inside network wey you control and for the same box with the other services wey you run. The cost difference for small scale dey real, but na no be the only thing to consider: hosted console na another person uptime, while your VPS na your own, including the night wey disk go full. If the box go pay for itself either way, other things wey you fit run for VPS na the list to read next.

FAQ

My UniFi device no dey adopt to controller for VPS, why?

Devices dey discover controllers by broadcasting for UDP port 10001. Broadcast no dey comot from local network, so device for remote site no fit find controller for public internet. Set inform host override for controller system settings to your VPS hostname. Then point the device go there with ssh ubnt@<device-ip> followed by set-inform http://vps.example.com:8080/inform. If device dey Adopting state, run set-inform again while e dey there. If another controller adopt am before, reset am to factory default first, because e still get the old controller credentials.

How much RAM self-hosted UniFi controller need?

Two GB na the working floor, while 4 GB go comfortable. The application na Java plus MongoDB, and each one dey size memory separately. The container image dey cap Java heap for 1024 MB by default, while MongoDB WiredTiger cache dey take half of the RAM wey pass 1 GB. For x86_64, also confirm say CPU dey expose AVX with grep -m1 -o avx /proc/cpuinfo, because MongoDB 5.0 and later no go start without am, and the database container go dey restart for loop.

I suppose expose port 8443 to internet?

No. Port 8443 na the admin interface, and e hold configuration for every site wey the controller dey manage. Publish am on 127.0.0.1 and reach am with ssh -L 8443:127.0.0.1:8443 you@vps.example.com, or bind am to WireGuard or Tailscale address. Na only TCP 8080 and UDP 3478 need make your sites reach dem, and you fit restrict dem to the sites' public addresses when dem dey static. Remember say Docker published port no dey get filtered by ufw, so test from outside machine instead of trusting ufw status.

My network go stop working if VPS controller go down?

No. Adopted access points and switches go continue forwarding traffic with the configuration wey controller don already push, so clients go remain connected and Wi-Fi go continue working. Na management go stop. You go lose dashboard and statistics collection, plus any live feature wey controller dey serve, like guest portal authentication or RADIUS when controller na the RADIUS server.

Where UniFi controller dey store automatic backups?

For the container image wey we use here, dem dey land for /config/data/backup/autobackup. E map to your data path plus data/backup/autobackup for the host, as .unf files wey dem name after the version and timestamp. For some fresh installs, autobackup directory no dey exist. The scheduled backup go then write nothing without reporting error. List that directory one day after you set schedule, and create am yourself if e empty. Copy the files comot from VPS, because .unf contains the site configuration and administrator accounts.

#unifi#ubiquiti#network-management#docker#self-hosting