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 how much RAM?
Two GB na the minimum, and 4 GB na the amount wey you suppose buy. Two things dey use memory for one box: Java and MongoDB. Each one dey manage the memory wey e need independently.
MEM_LIMIT dey limit the Java heap, and the container image set am to 1024 MB by default. MongoDB na the other one. E 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 one na roughly 512 MB cache plus 1 GB heap plus JVM own non-heap memory plus operating system memory. E go fit work until traffic and activity become high, then kernel out-of-memory killer go stop one of the two processes. After any restart wey you no understand, 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 too 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 how many clients 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 other service first, because PhotoPrism and Immich get very different RAM minimums and either one need more than the controller.
One CPU feature matter, and e easy to miss for cheap plan:
grep -m1 -o avx /proc/cpuinfoMongoDB 5.0 and later need AVX (advanced vector extensions) for x86_64 hardware. If that command no print anything, 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 guest fit cause am too. MongoDB 4.4 na the newest release wey run without AVX, and the image accept older ones reach 3.6, but 4.4 reach end of life for February 2024. MongoDB did ship 4.4 builds for e December 2025 fix for CVE-2025-14847, but nothing promise say dem go do the same for the next flaw. The older releases dey further behind. Moving to host wey get newer CPU na the better answer. For ARM VPS, this question no come up, because AVX na x86 instruction set, and both images publish arm64 builds. If you dey choose between the two, the 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 ~/unifiMongoDB 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" }
]
})
EOFThat 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-stoppedDem 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-applicationdocker 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/informAny 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 for Docker Engine 28.0.0 or newer, nothing outside the VPS fit reach admin interface. For older releases, other hosts for the same L2 segment, like machines wey dey on the same network switch, fit reach ports wey dem publish to localhost. So run docker version and upgrade first if server report say e dey run older version. Forward am through SSH to run setup wizard:
ssh -L 8443:127.0.0.1:8443 you@vps.example.comLeave that session open and open https://127.0.0.1:8443 for browser. The certificate na self-signed, so browser go show warning once. Create administrator account, give the site name, and skip device adoption for now.
SSH tunnel dey okay for one administrator. For team, give 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 thing be say Docker no fit publish on address wey never exist yet. So tunnel interface must come up before container start, or 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/informNewer 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/informThe 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 be UniFi ports wey you suppose open, and wey you suppose keep private
Two ports need to dey reachable from remote site for adoption and everyday management.
- TCP 8080 na the inform channel, and every adopted device dey connect to am. The payload inside dey AES encrypted with key wey controller give the device during adoption, na why plain HTTP be the normal setting for here.
- UDP 3478 na STUN (session traversal utilities for NAT), wey devices dey use to keep path back to controller.
Some features need more ports. Ubiquiti port reference add TCP 5671 for Traffic Flow logging from UXG gateways wey dem adopt for L2 or L3 networks. So, if site get UXG wey dey report to this controller and you want those logs, open 5671 to that site too.
Every other thing suppose remain closed for VPS.
- TCP 8443 na the admin interface. This one must never be public. E hold configuration for every site wey controller dey manage, all behind one password.
- UDP 10001 and UDP 1900 na broadcast discovery. Broadcast no dey cross internet, so opening dem no go 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 no publish any port, so e dey only for internal Docker network. Keep am like that.
If your sites get static public addresses, allow only those ones:
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 verbosethe ufw basics for 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 port dey write NAT and forwarding rules directly into iptables, and Docker own chain dey filter that traffic, no be 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 8443Refusal or timeout na wetin you want. If e connect, the port dey public no matter wetin ufw dey say. The reliable fix na the one wey already dey for compose file: publish the port on 127.0.0.1 or on tunnel address, so Docker no go bind am to public interface. Rule for DOCKER-USER chain fit work too, but binding simpler, and mistake for rule ordering no fit undo am.
Wetin about Ubiquiti own installers?
Ubiquiti dey publish Debian package for Network Application, and as of October 2026, their help center label that method legacy and call UniFi OS Server the new standard for self-hosting. The package dey work, but for current Ubuntu e raise MongoDB question wey the distribution no longer answer: Ubuntu 22.04 and 24.04 no ship MongoDB server package, so you go need add MongoDB own repository and match the versions by hand. The container above dey handle that matching inside one pinned tag, na why we choose this method 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 October 2026, Ubiquiti list x86-64 CPU, Ubuntu 24.04 or later or Debian 13 or later, Podman 4.9.3 or newer with slirp4netns 1.2 or newer, at least 2 GB of RAM, and 10 GB of free storage. The installer link for Ubiquiti Releases or Download page dey tied to one version: right-click the download, copy the link address, then fetch am with curl -O or wget. Every release get its own URL, so guide fit pin one version but e no fit give you link wey always point to the latest. The installer dey set up systemd service called uosserver, wey you fit stop and start with systemctl. Choose am if you want the vendor own packaging. Choose the container stack if you want pin versions by yourself and keep the box 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/autobackupIf 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-applicationA .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-applicationNa 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 service. As of October 2026, Ubiquiti US store dey sell am as Official UniFi Hosting, starting from $29 per month. E fit manage up to 1,000 UniFi Network devices for server wey Ubiquiti dey run. The self-hosted application wey you just install na free, and e no get subscription.
Choose the hosted console if na one site you dey manage and you prefer to 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 other services wey you dey 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 fill up. If the box go make money for either case, other things wey you fit run for VPS na the list to read next.
FAQ
Why my UniFi device no go adopt to controller for VPS?
Devices dey discover controllers by broadcasting for UDP port 10001. Broadcast no dey comot from local network, so device wey dey remote site no fit find controller for public internet. Set the inform host override for the controller's 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 the device remain for 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 hold the old controller's credentials.
How much RAM self-hosted UniFi controller need?
Two GB na the working floor, while 4 GB dey comfortable. The application na Java plus MongoDB, and both of dem size their memory separately. The container image cap Java heap to 1024 MB by default, while MongoDB's WiredTiger cache take half of the RAM wey pass 1 GB. For x86_64, confirm too say the CPU 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 manage. Publish am on 127.0.0.1 with Docker Engine 28.0.0 or later, then reach am with ssh -L 8443:127.0.0.1:8443 you@vps.example.com. Or bind am to WireGuard or Tailscale address. For adoption and management, your sites need reach TCP 8080 and UDP 3478. Dem also need TCP 5671 if UXG gateway dey send Traffic Flow logs to the controller. You fit restrict these ports to the sites' public addresses when the addresses dey static. Remember say Docker published port no dey filtered by ufw, so test from outside machine instead of trusting ufw status.
My network go stop working if the VPS controller go down?
No. Adopted access points and switches go continue forward traffic with the configuration wey the controller already push, so clients go remain connected and Wi-Fi go continue work. Na management go stop. You go lose the dashboard and statistics collection, plus any live feature wey the controller dey serve, such as guest portal authentication or RADIUS when the 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. This path map to your data path plus data/backup/autobackup on the host, as .unf files wey dem name after the version and a timestamp. For some fresh installs, autobackup directory no dey exist. When that happen, scheduled backup go 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 the VPS, because .unf contain the site configuration and administrator accounts.